/* * Copyright (C)2005-2019 Haxe Foundation * * 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. */ import php.*; import php.ArrayIterator as NativeArrayIterator; import haxe.iterators.ArrayKeyValueIterator; using php.Global; @:coreApi final class Array implements ArrayAccess implements IteratorAggregate implements Countable implements JsonSerializable> { public var length(default, null):Int; var arr:NativeIndexedArray; public function new() { arr = new NativeIndexedArray(); length = 0; } public function concat(a:Array):Array { return wrap(Global.array_merge(arr, a.arr)); } public inline function copy():Array { return Syntax.clone(this); } public inline function filter(f:T->Bool):Array { var result = Syntax.arrayDecl(); for(item in arr) { if (f(item)) { result.push(item); } } return wrap(result); } public inline function contains(x:T):Bool { return indexOf(x) != -1; } public function indexOf(x:T, ?fromIndex:Int):Int { if (fromIndex == null && !Boot.isHxClosure(x) && !Boot.isNumber(x)) { var index = Global.array_search(x, arr, true); if (index == false) { return -1; } else { return index; } } if (fromIndex == null) { fromIndex = 0; } else { if (fromIndex < 0) fromIndex += length; if (fromIndex < 0) fromIndex = 0; } while (fromIndex < length) { if (arr[fromIndex] == x) return fromIndex; fromIndex++; } return -1; } public function insert(pos:Int, x:T):Void { length++; Global.array_splice(arr, pos, 0, Syntax.arrayDecl(x)); } @:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator") public inline function iterator():haxe.iterators.ArrayIterator { return new haxe.iterators.ArrayIterator(this); } @:keep public inline function keyValueIterator():ArrayKeyValueIterator { return new ArrayKeyValueIterator(this); } public function join(sep:String):String { return Global.implode(sep, Global.array_map(Syntax.nativeClassName(Boot) + '::stringify', arr)); } public function lastIndexOf(x:T, ?fromIndex:Int):Int { if (fromIndex == null || fromIndex >= length) fromIndex = length - 1; if (fromIndex < 0) fromIndex += length; while (fromIndex >= 0) { if (arr[fromIndex] == x) return fromIndex; fromIndex--; } return -1; } public inline function map(f:T->S):Array { var result = Syntax.arrayDecl(); for(item in arr) { result.push(f(item)); } return wrap(result); } public inline function pop():Null { if (length > 0) length--; return Global.array_pop(arr); } public inline function push(x:T):Int { arr[length++] = x; return length; } public function remove(x:T):Bool { var result = false; for(index in 0...length) { if (arr[index] == x) { Global.array_splice(arr, index, 1); length--; result = true; break; } } return result; } public inline function reverse():Void { arr = Global.array_reverse(arr); } public inline function shift():Null { if (length > 0) length--; return Global.array_shift(arr); } public function slice(pos:Int, ?end:Int):Array { if (pos < 0) pos += length; if (pos < 0) pos = 0; if (end == null) { return wrap(Global.array_slice(arr, pos)); } else { if (end < 0) end += length; if (end <= pos) { return []; } else { return wrap(Global.array_slice(arr, pos, end - pos)); } } } public inline function sort(f:T->T->Int):Void { arr.usort(f); } public function splice(pos:Int, len:Int):Array { if (len < 0) return []; var result = wrap(Global.array_splice(arr, pos, len)); length -= result.length; return result; } public inline function unshift(x:T):Void { length = Global.array_unshift(arr, x); } public function toString():String { return inline Boot.stringifyNativeIndexedArray(arr); } public function resize(len:Int):Void { if (length < len) { arr = Global.array_pad(arr, len, null); } else if (length > len) { Global.array_splice(arr, len, length - len); } length = len; } @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') function offsetExists(offset:Int):Bool { return offset < length; } @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') function offsetGet(offset:Int):Ref { try { return arr[offset]; } catch (e:Dynamic) { return null; } } @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') function offsetSet(offset:Int, value:T):Void { if (length <= offset) { for(i in length...offset + 1) { arr[i] = null; } length = offset + 1; } arr[offset] = value; Syntax.code("return {0}", value); } @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') function offsetUnset(offset:Int):Void { if (offset >= 0 && offset < length) { Global.array_splice(arr, offset, 1); --length; } } @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') private function getIterator():Traversable { return new NativeArrayIterator(arr); } @:noCompletion @:keep @:native('count') //to not interfere with `Lambda.count` @:php.attribute('\\ReturnTypeWillChange') private function _hx_count():Int { return length; } @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') function jsonSerialize():NativeIndexedArray { return arr; } static function wrap(arr:NativeIndexedArray):Array { var a = new Array(); a.arr = arr; a.length = Global.count(arr); return a; } } /** Following interfaces are required to make `Array` mimic native arrays for usage from a 3rd party PHP code. **/ @:native('ArrayAccess') private extern interface ArrayAccess { private function offsetExists(offset:K):Bool; private function offsetGet(offset:K):V; private function offsetSet(offset:K, value:V):Void; private function offsetUnset(offset:K):Void; } @:native('JsonSerializable') private extern interface JsonSerializable { private function jsonSerialize():T; } @:native('IteratorAggregate') private extern interface IteratorAggregate extends Traversable { private function getIterator():Traversable; } @:native('Countable') private extern interface Countable { @:native('count') //to not interfere with `Lambda.count` private function _hx_count():Int; }