2828use pocketmine \nbt \NbtStreamReader ;
2929use pocketmine \nbt \NbtStreamWriter ;
3030use pocketmine \nbt \ReaderTracker ;
31- use function assert ;
31+ use function array_key_last ;
32+ use function array_map ;
33+ use function array_pop ;
34+ use function array_push ;
35+ use function array_shift ;
36+ use function array_slice ;
37+ use function array_unshift ;
38+ use function array_values ;
3239use function count ;
3340use function func_num_args ;
3441use function get_class ;
35- use function iterator_to_array ;
3642use function str_repeat ;
3743
3844/**
@@ -44,20 +50,19 @@ final class ListTag extends Tag implements \Countable, \IteratorAggregate{
4450 /** @var int */
4551 private $ tagType ;
4652 /**
47- * @var \SplDoublyLinkedList| Tag[]
48- * @phpstan-var \SplDoublyLinkedList <Tag>
53+ * @var Tag[]
54+ * @phpstan-var list <Tag>
4955 */
50- private $ value ;
56+ private $ value = [] ;
5157
5258 /**
5359 * @param Tag[] $value
5460 */
5561 public function __construct (array $ value = [], int $ tagType = NBT ::TAG_End){
5662 self ::restrictArgCount (__METHOD__ , func_num_args (), 2 );
5763 $ this ->tagType = $ tagType ;
58- $ this ->value = new \SplDoublyLinkedList ();
5964 foreach ($ value as $ tag ){
60- $ this ->push ($ tag );
65+ $ this ->push ($ tag ); //ensure types get checked
6166 }
6267 }
6368
@@ -66,12 +71,7 @@ public function __construct(array $value = [], int $tagType = NBT::TAG_End){
6671 * @phpstan-return list<Tag>
6772 */
6873 public function getValue () : array {
69- $ value = [];
70- foreach ($ this ->value as $ v ){
71- $ value [] = $ v ;
72- }
73-
74- return $ value ;
74+ return $ this ->value ;
7575 }
7676
7777 /**
@@ -80,50 +80,51 @@ public function getValue() : array{
8080 * @phpstan-return list<mixed>
8181 */
8282 public function getAllValues () : array {
83- $ result = [];
84- foreach ($ this ->value as $ tag ){
85- $ result [] = $ tag ->getValue ();
86- }
87-
88- return $ result ;
83+ return array_map (fn (Tag $ t ) => $ t ->getValue (), $ this ->value );
8984 }
9085
9186 public function count () : int {
92- return $ this ->value -> count ( );
87+ return count ( $ this ->value );
9388 }
9489
9590 public function getCount () : int {
96- return $ this ->value -> count ( );
91+ return count ( $ this ->value );
9792 }
9893
9994 /**
10095 * Appends the specified tag to the end of the list.
10196 */
10297 public function push (Tag $ tag ) : void {
10398 $ this ->checkTagType ($ tag );
104- $ this ->value -> push ( $ tag) ;
99+ $ this ->value [] = $ tag ;
105100 }
106101
107102 /**
108103 * Removes the last tag from the list and returns it.
109104 */
110105 public function pop () : Tag {
111- return $ this ->value ->pop ();
106+ if (count ($ this ->value ) === 0 ){
107+ throw new \LogicException ("List is empty " );
108+ }
109+ return array_pop ($ this ->value );
112110 }
113111
114112 /**
115113 * Adds the specified tag to the start of the list.
116114 */
117115 public function unshift (Tag $ tag ) : void {
118116 $ this ->checkTagType ($ tag );
119- $ this ->value -> unshift ( $ tag );
117+ array_unshift ( $ this ->value , $ tag );
120118 }
121119
122120 /**
123121 * Removes the first tag from the list and returns it.
124122 */
125123 public function shift () : Tag {
126- return $ this ->value ->shift ();
124+ if (count ($ this ->value ) === 0 ){
125+ throw new \LogicException ("List is empty " );
126+ }
127+ return array_shift ($ this ->value );
127128 }
128129
129130 /**
@@ -135,14 +136,23 @@ public function shift() : Tag{
135136 */
136137 public function insert (int $ offset , Tag $ tag ){
137138 $ this ->checkTagType ($ tag );
138- $ this ->value ->add ($ offset , $ tag );
139+ if ($ offset < 0 || $ offset > count ($ this ->value )){
140+ throw new \OutOfRangeException ("Offset cannot be negative or larger than the list's current size " );
141+ }
142+ $ newValue = array_slice ($ this ->value , 0 , $ offset );
143+ $ newValue [] = $ tag ;
144+ array_push ($ newValue , ...array_slice ($ this ->value , $ offset ));
145+ $ this ->value = $ newValue ;
139146 }
140147
141148 /**
142149 * Removes a value from the list. All later tags in the list are moved down by 1 position.
143150 */
144151 public function remove (int $ offset ) : void {
145- unset($ this ->value [$ offset ]);
152+ //to keep phpstan happy we can't directly unset from $this->value
153+ $ newValue = $ this ->value ;
154+ unset($ newValue [$ offset ]);
155+ $ this ->value = array_values ($ newValue );
146156 }
147157
148158 /**
@@ -161,14 +171,20 @@ public function get(int $offset) : Tag{
161171 * Returns the element in the first position of the list, without removing it.
162172 */
163173 public function first () : Tag {
164- return $ this ->value ->bottom ();
174+ if (count ($ this ->value ) === 0 ){
175+ throw new \LogicException ("List is empty " );
176+ }
177+ return $ this ->value [0 ];
165178 }
166179
167180 /**
168181 * Returns the element in the last position in the list (the end), without removing it.
169182 */
170183 public function last () : Tag {
171- return $ this ->value ->top ();
184+ if (count ($ this ->value ) === 0 ){
185+ throw new \LogicException ("List is empty " );
186+ }
187+ return $ this ->value [array_key_last ($ this ->value )];
172188 }
173189
174190 /**
@@ -178,6 +194,9 @@ public function last() : Tag{
178194 */
179195 public function set (int $ offset , Tag $ tag ) : void {
180196 $ this ->checkTagType ($ tag );
197+ if ($ offset < 0 || $ offset > count ($ this ->value )){ //allow setting the end offset
198+ throw new \OutOfRangeException ("Offset cannot be negative or larger than the list's current size " );
199+ }
181200 $ this ->value [$ offset ] = $ tag ;
182201 }
183202
@@ -192,7 +211,7 @@ public function isset(int $offset) : bool{
192211 * Returns whether there are any tags in the list.
193212 */
194213 public function empty () : bool {
195- return $ this ->value -> isEmpty () ;
214+ return count ( $ this ->value ) === 0 ;
196215 }
197216
198217 protected function getTypeName () : string {
@@ -218,7 +237,7 @@ public function getTagType() : int{
218237 * @throws \LogicException if the list is not empty
219238 */
220239 public function setTagType (int $ type ){
221- if (! $ this ->value -> isEmpty () ){
240+ if (count ( $ this ->value ) > 0 ){
222241 throw new \LogicException ("Cannot change tag type of non-empty ListTag " );
223242 }
224243 $ this ->tagType = $ type ;
@@ -264,31 +283,22 @@ public static function read(NbtStreamReader $reader, ReaderTracker $tracker) : s
264283
265284 public function write (NbtStreamWriter $ writer ) : void {
266285 $ writer ->writeByte ($ this ->tagType );
267- $ writer ->writeInt ($ this ->value ->count ());
268- /** @var Tag $tag */
286+ $ writer ->writeInt (count ($ this ->value ));
269287 foreach ($ this ->value as $ tag ){
270288 $ tag ->write ($ writer );
271289 }
272290 }
273291
274292 protected function stringifyValue (int $ indentation ) : string {
275293 $ str = "{ \n" ;
276- /** @var Tag $tag */
277294 foreach ($ this ->value as $ tag ){
278295 $ str .= str_repeat (" " , $ indentation + 1 ) . $ tag ->toString ($ indentation + 1 ) . "\n" ;
279296 }
280297 return $ str . str_repeat (" " , $ indentation ) . "} " ;
281298 }
282299
283300 public function __clone (){
284- /** @phpstan-var \SplDoublyLinkedList<Tag> $new */
285- $ new = new \SplDoublyLinkedList ();
286-
287- foreach ($ this ->value as $ tag ){
288- $ new ->push ($ tag ->safeClone ());
289- }
290-
291- $ this ->value = $ new ;
301+ $ this ->value = array_map (fn (Tag $ t ) => $ t ->safeClone (), $ this ->value );
292302 }
293303
294304 protected function makeCopy (){
@@ -300,20 +310,16 @@ protected function makeCopy(){
300310 * @phpstan-return \Generator<int, Tag, void, void>
301311 */
302312 public function getIterator () : \Generator {
303- //we technically don't need iterator_to_array() here, but I don't feel comfortable relying on "yield from" to
304- //copy the underlying dataset referenced by SplDoublyLinkedList
305- yield from iterator_to_array ($ this ->value , true );
313+ yield from $ this ->value ;
306314 }
307315
308316 public function equals (Tag $ that ) : bool {
309317 if (!($ that instanceof $ this ) or count ($ this ->value ) !== count ($ that ->value )){
310318 return false ;
311319 }
312320
313- $ thatValues = $ that ->getValue (); //SplDoublyLinkedList has O(n) random access, so this is faster than repeated get() calls
314321 foreach ($ this ->value as $ k => $ v ){
315- assert (isset ($ thatValues [$ k ]), "We checked the count above, so this should not be missing " );
316- if (!$ v ->equals ($ thatValues [$ k ])){
322+ if (!$ v ->equals ($ that ->value [$ k ])){
317323 return false ;
318324 }
319325 }
0 commit comments