Squashed 'cloner/' content from commit 79ff709

git-subtree-dir: cloner
git-subtree-split: 79ff709d0060b967d3508b6abfe194bedfb63f1b
This commit is contained in:
2020-12-09 18:33:53 -07:00
parent 6df188f432
commit a47af32adb
23 changed files with 431 additions and 4 deletions

2
.gitignore vendored
View File

@@ -1 +1 @@
bin/
bin/

View File

@@ -36,4 +36,4 @@ install:
curl -sSLf https://lib.haxe.org/p/jQueryExtern/3.2.1/download/ -o /dev/null;
fi
script: ./test.sh
script: ./test.sh

View File

@@ -71,4 +71,4 @@ At the next milestone:
Hopefully someday:
* Professional game developers who want to learn the ways of Lisp
* Professional game developers who want to learn the ways of Lisp

1
cloner/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
bin/test/

13
cloner/.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
language: objective-c
install:
- brew install neko haxe
- cd ..
- mkdir haxelib
- haxelib setup haxelib
- haxelib install hxcpp > /dev/null
- haxelib install hx3compat > /dev/null
before_script:
- cd $TRAVIS_BUILD_DIR
script:
- sh test.sh -e

24
cloner/LICENSE.md Normal file
View File

@@ -0,0 +1,24 @@
License
=======
The MIT License (MIT)
Copyright (c) 2015 Thomas Uster
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

12
cloner/README.md Normal file
View File

@@ -0,0 +1,12 @@
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE.md)
[![TravisCI Build Status](https://travis-ci.org/thomasuster/cloner.svg?branch=master)](https://travis-ci.org/thomasuster/cloner)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/thomasuster/cloner?branch=master&svg=true)](https://ci.appveyor.com/project/thomasuster/cloner)
```
git clone https://github.com/thomasuster/cloner.git
haxelib dev cloner cloner/src
```
```
var cloner:Cloner = new Cloner();
var clone = cloner.clone(mostAnything);
```

33
cloner/appveyor.yml Normal file
View File

@@ -0,0 +1,33 @@
version: "{build}"
clone_folder: C:\projects\cloner
environment:
global:
HAXELIB_ROOT: C:\projects\haxelib
install:
# Install haxe and neko
- cinst neko --version 2.2.0 -y
- cinst haxe -y
- RefreshEnv
- neko -version
- haxe -version
# Setup haxelib
- mkdir "%HAXELIB_ROOT%"
- haxelib setup "%HAXELIB_ROOT%"
- haxelib install record-macros
- haxelib install hxcpp
build_script:
- cd test
- haxe compile.hxml
- cd ..
test_script:
- echo CPP
- bin\test\TestMain-debug.exe
- echo NEKO
- neko bin\test\TestMain.n

13
cloner/haxelib.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "cloner",
"url" : "https://github.com/thomasuster/cloner",
"license": "MIT",
"tags": ["clone", "copy", "deep"],
"description": "A deep copy algorithm for haxe",
"version": "1.0.0",
"classPath": "src/",
"releasenote": "",
"contributors": ["thomasuster"],
"dependencies": {
}
}

109
cloner/src/cloner/Cloner.hx Normal file
View File

@@ -0,0 +1,109 @@
package cloner;
import Array;
import haxe.ds.ObjectMap;
import Type.ValueType;
import haxe.ds.IntMap;
import haxe.ds.StringMap;
class Cloner {
var cache:ObjectMap<Dynamic,Dynamic>;
var classHandles:Map<String,Dynamic->Dynamic>;
var stringMapCloner:MapCloner<String>;
var intMapCloner:MapCloner<Int>;
public function new():Void {
stringMapCloner = new MapCloner(this,StringMap);
intMapCloner = new MapCloner(this,IntMap);
classHandles = new Map<String,Dynamic->Dynamic>();
classHandles.set('String',returnString);
classHandles.set('Array',cloneArray);
classHandles.set('haxe.ds.StringMap',stringMapCloner.clone);
classHandles.set('haxe.ds.IntMap',intMapCloner.clone);
}
function returnString(v:String):String {
return v;
}
public function clone <T> (v:T):T {
cache = new ObjectMap<Dynamic,Dynamic>();
var outcome:T = _clone(v);
cache = null;
return outcome;
}
public function _clone <T> (v:T):T {
#if js
if(Std.is(v, String))
return v;
#end
#if neko
try {
if(Type.getClassName(cast v) != null)
return v;
}catch(e:Dynamic) {}
#else
if(Type.getClassName(cast v) != null)
return v;
#end
switch(Type.typeof(v)){
case TNull:
return null;
case TInt:
return v;
case TFloat:
return v;
case TBool:
return v;
case TObject:
return handleAnonymous(v);
case TFunction:
return null;
case TClass(c):
if(!cache.exists(v))
cache.set(v,handleClass(c, v));
return cache.get(v);
case TEnum(e):
return v;
case TUnknown:
return null;
}
}
function handleAnonymous (v:Dynamic):Dynamic {
var properties:Array<String> = Reflect.fields(v);
var anonymous:Dynamic = {};
for (i in 0...properties.length) {
var property:String = properties[i];
Reflect.setField(anonymous, property, _clone(Reflect.getProperty(v, property)));
}
return anonymous;
}
function handleClass <T> (c:Class<T>,inValue:T):T {
var handle:T->T = classHandles.get(Type.getClassName(c));
if(handle == null)
handle = cloneClass;
return handle(inValue);
}
function cloneArray <T> (inValue:Array<T>):Array<T> {
var array:Array<T> = inValue.copy();
for (i in 0...array.length)
array[i] = _clone(array[i]);
return array;
}
function cloneClass <T> (inValue:T):T {
var outValue:T = Type.createEmptyInstance(Type.getClass(inValue));
var fields:Array<String> = Reflect.fields(inValue);
for (i in 0...fields.length) {
var field = fields[i];
var property = Reflect.getProperty(inValue, field);
Reflect.setField(outValue, field, _clone(property));
}
return outValue;
}
}

View File

@@ -0,0 +1,25 @@
package cloner;
import haxe.Constraints.IMap;
import cloner.Cloner;
import Type.ValueType;
class MapCloner<K>{
var cloner:Cloner;
var type:Class<IMap<K,Dynamic>>;
var noArgs:Array<Dynamic>;
public function new(cloner:Cloner, type:Class<IMap<K,Dynamic>>):Void {
this.cloner = cloner;
this.type = type;
noArgs = [];
}
public function clone <K,Dynamic> (inValue:IMap<K,Dynamic>):IMap<K,Dynamic> {
var inMap:IMap<K,Dynamic> = inValue;
var map:IMap<K,Dynamic> = cast Type.createInstance(type, noArgs);
for (key in inMap.keys()) {
map.set(key, cloner._clone(inMap.get(key)));
}
return map;
}
}

8
cloner/test.sh Normal file
View File

@@ -0,0 +1,8 @@
set -e
cd test
haxe compile.hxml
cd ..
echo CPP
bin/test/TestMain-debug
echo NEKO
neko bin/test/TestMain.n

94
cloner/test/TestCloner.hx Normal file
View File

@@ -0,0 +1,94 @@
import classes.ClassTypeProperty;
import classes.ABCEnum;
import cloner.Cloner;
import classes.ArrayProperty;
import classes.ClassProperty;
import classes.BoolProperty;
import classes.NullClass;
class TestCloner extends haxe.unit.TestCase {
var cloner:Cloner;
override public function setup() {
cloner = new Cloner();
}
public function testclone() {
var value:NullClass = new NullClass();
assertTrue(cloner.clone(value) != null);
}
public function testBoolProperty() {
var value:BoolProperty = new BoolProperty(true);
assertTrue(cloner.clone(value).property);
}
public function testArrayProperty() {
var value:ArrayProperty = new ArrayProperty([true]);
assertTrue(cloner.clone(value).property[0]);
}
public function testStringMap():Void {
var value:Map<String, Int> = new Map<String, Int>();
value.set('you',1);
assertTrue(cloner.clone(value).get('you') == 1);
}
public function testIntMap():Void {
var value:Map<Int, Int> = new Map<Int, Int>();
value.set(1,2);
assertTrue(cloner.clone(value).get(1) == 2);
}
public function testObjectsInMap():Void {
var value:Map<Int, NullClass> = new Map<Int, NullClass>();
var v:NullClass = new NullClass();
value.set(1,v);
assertTrue(cloner.clone(value).get(1) != v);
}
public function testString():Void {
assertTrue(cloner.clone('a') == 'a');
}
public function testClassType():Void {
assertTrue(cloner.clone(NullClass) == NullClass);
}
public function testEnum():Void {
assertTrue(cloner.clone(ABCEnum.b) == ABCEnum.b);
}
public function testClassProperty():Void {
var value:ClassProperty = new ClassProperty();
var inValue = new BoolProperty(true);
value.property = inValue;
var outValue:BoolProperty = cloner.clone(value).property;
assertTrue(outValue.property);
assertFalse(inValue == outValue);
}
public function testAnonObjectAreDifferentCopies():Void {
var value = { a: 10, b: 20 };
var clone = cloner.clone(value);
Reflect.setField(value, 'a', 33);
assertEquals(10, Reflect.getProperty(clone, 'a'));
}
public function testEmbededAnonObject():Void {
var inner = {b: 5};
var value = { a: 10, inner: inner };
var clone = cloner.clone(value);
Reflect.setField(inner, 'b', 33);
var cloneInner = Reflect.getProperty(clone, 'inner');
assertEquals(5, Reflect.getProperty(cloneInner, 'b'));
}
public function testClassRefInsideAnObj():Void {
var value:ClassTypeProperty = new ClassTypeProperty();
value.property = BoolProperty;
var outValue:ClassTypeProperty = cloner.clone(value);
assertEquals(outValue.property, BoolProperty);
}
}

View File

@@ -0,0 +1,35 @@
import cloner.Cloner;
import classes.ClassProperty;
import classes.BoolProperty;
class TestClonerCache extends haxe.unit.TestCase {
var cloner:Cloner;
var input:Array<ClassProperty>;
var outcome:Array<ClassProperty>;
override public function setup() {
cloner = new Cloner();
}
public function testReferences():Void {
setupOutcomeWithReferences();
assertTrue(outcome[0].property == outcome[1].property);
}
public function testNoSideEffects():Void {
setupOutcomeWithReferences();
outcome[0].property = true;
outcome = cloner.clone(input);
assertFalse(outcome[0].property);
}
function setupOutcomeWithReferences():Void {
var a:ClassProperty = new ClassProperty();
var b:ClassProperty = new ClassProperty();
var c:BoolProperty = new BoolProperty();
a.property = c;
b.property = c;
input = [a, b];
outcome = cloner.clone(input);
}
}

11
cloner/test/TestMain.hx Normal file
View File

@@ -0,0 +1,11 @@
import haxe.unit.TestRunner;
class TestMain {
static function main() {
var runner:TestRunner = new TestRunner();
runner.add(new TestCloner());
runner.add(new TestClonerCache());
runner.run();
}
}

View File

@@ -0,0 +1,6 @@
package classes;
enum ABCEnum {
a;
b;
c;
}

View File

@@ -0,0 +1,7 @@
package classes;
class ArrayProperty {
public var property:Array<Bool>;
public function new(v:Array<Bool>):Void {
property = v;
}
}

View File

@@ -0,0 +1,7 @@
package classes;
class BoolProperty {
public var property:Bool;
public function new(v:Bool=false):Void {
property = v;
}
}

View File

@@ -0,0 +1,5 @@
package classes;
class ClassProperty {
public var property:Dynamic;
public function new():Void {}
}

View File

@@ -0,0 +1,5 @@
package classes;
class ClassTypeProperty {
public var property:Class<Dynamic>;
public function new():Void {}
}

View File

@@ -0,0 +1,4 @@
package classes;
class NullClass {
public function new():Void {}
}

15
cloner/test/compile.hxml Normal file
View File

@@ -0,0 +1,15 @@
-cp .
-cp ../src
-lib hx3compat
-main TestMain
-debug
-cpp ../bin/test
--next
-cp .
-cp ../src
-lib hx3compat
-main TestMain
-debug
-neko ../bin/test/TestMain.n

View File

@@ -15,4 +15,4 @@ else
fi
haxe kiss/build-scripts/common-args.hxml kiss/build-scripts/common-test-args.hxml kiss/build-scripts/$KISS_TARGET/test.hxml
fi
fi