# Copyright (c) 2023 Yuki Kimoto # MIT License class List { use Fn; use Array; # Enumerations enum { DEFAULT_CAPACITY = 4, } # Fields has capacity : ro int; has length : ro int; has values : ro object[]; # Class methods static method new : List ($array = undef : object[], $capacity = -1 : int) { my $length : int; if ($array) { $length = @$array; } else { $length = 0; } my $self = &new_len($array, $length, $capacity); if ($array) { Array->memcpy_object_address($self->{values}, 0, $array, 0, $length); } return $self; } static method new_len : List ($proto_array : object[], $length : int, $capacity = -1 : int) { my $self = new List; unless ($proto_array) { $proto_array = new object[0]; } unless ($length >= 0) { die "The \$length must be greater than or equal to 0"; } if ($capacity < 0) { $capacity = &DEFAULT_CAPACITY; } if ($length > $capacity) { $capacity = $length; } $self->{capacity} = $capacity; $self->{length} = $length; $self->{values} = Array->new_proto($proto_array, $capacity); return $self; } # Instance methods method get : object ($index : int) { my $length = $self->length; unless ($index >= 0) { die "The \$index must be greater than or equal to 0"; } unless ($index < $length) { die "The \$index must be less than the length of the \$list"; } my $element = $self->{values}[$index]; return $element; } method insert : void ($index : int, $element : object) { my $length = $self->{length}; my $capacity = $self->{capacity}; unless ($index >= 0) { die "The \$index must be greater than or equal to 0"; } unless ($index <= $length) { die "The \$index must be less than or equal to the length of the \$list"; } my $new_length = $length + 1; $self->_maybe_extend($new_length); my $elements = $self->{values}; if ($index != $length) { Array->memmove_object_address($elements, $index + 1, $elements, $index, $length - $index); } $elements->[$index] = $element; $self->{length}++; } method pop : object () { my $length = $self->length; unless ($length > 0) { die "The length of the \$list must be greater than 0"; } my $index = $self->{length}; my $ret = $self->{values}[$index - 1]; $self->{values}[$index - 1] = undef; --$self->{length}; return $ret; } method push : void ($element : object) { my $length = $self->{length}; my $capacity = $self->{capacity}; my $new_length = $length + 1; $self->_maybe_extend($new_length); my $index = $self->{length}; $self->{values}[$index] = $element; ++$self->{length}; } method remove : object ($index : int) { my $length = $self->{length}; my $capacity = $self->{capacity}; unless ($index >= 0) { die "The \$index must be greater than or equal to 0"; } unless ($index < $length) { die "The \$index must be less than the length of the \$list"; } my $elements = $self->{values}; my $remove_value = $elements->[$index]; my $move_length = $length - $index - 1; Array->memmove_object_address($elements, $index, $elements, $index + 1, $move_length); $elements->[$length - 1] = undef; $self->{length}--; return $remove_value; } method replace : void ($offset : int, $remove_length : int, $replace : object[]) { unless ($offset >= 0) { die("The \$offset must be greater than or equal to 0"); } unless ($remove_length >= 0) { die("The \$remove_length must be greater than or equal to 0"); } unless ($offset + $remove_length <= $self->{length}) { die("The \$offset + the \$removing lenght must be less than or equal to the length of the \$list"); } my $replace_length = 0; if ($replace) { $replace_length = @$replace; } my $new_length = $self->{length} - $remove_length + $replace_length; $self->_maybe_extend($new_length); my $move_length = $self->{length} - $offset - $remove_length; Array->memmove_object_address($self->{values}, $offset + $replace_length, $self->{values}, $offset + $remove_length, $move_length); if ($replace) { Array->memcpy_object_address($self->{values}, $offset, $replace, 0, $replace_length); } $self->{length} = $new_length; } method reserve : void ($new_capacity : int) { unless ($new_capacity >= 0) { die "The \$new_capacity must be greater than or equal to 0"; } my $capacity = $self->{capacity}; if ($new_capacity > $capacity) { my $length = $self->{length}; my $new_values = new object[$new_capacity]; Array->memcpy_object_address($new_values, 0, $self->{values}, 0, $length); $self->{values} = $new_values; $self->{capacity} = $new_capacity; } } method resize : void ($new_length : int) { unless ($new_length >= 0) { die "The \$new_length must be greater than or equal to 0"; } my $length = $self->{length}; my $capacity = $self->{capacity}; if ($new_length > $length) { $self->_maybe_extend($new_length); } elsif ($new_length < $length) { Array->memset_object($self->{values}, undef, $new_length, $length - $new_length); } $self->{length} = $new_length; } method set : void ($index : int, $element : object) { my $length = $self->length; unless ($index >= 0) { die "The \$index must be greater than or equal to 0"; } unless ($index < $length) { die "The \$index must be less than the length of the \$list"; } $self->{values}[$index] = $element; } method set_array : void ($array : object[]) { unless ($array) { die "The \$array must be defined"; } my $cur_length = $self->length; my $set_length = @$array; unless ($set_length == $cur_length) { die "The length of the \$array must be the \$same as the length of the \$list"; } my $elements = $self->{values}; Array->memcpy_object_address($elements, 0, $array, 0, $cur_length); } method shift : object () { my $length = $self->{length}; my $capacity = $self->{capacity}; unless ($length > 0) { die "The length of the \$list must be greater than 0"; } my $elements = $self->{values}; my $element = $elements->[0]; Array->memmove_object_address($elements, 0, $elements, 1, $length - 1); $elements->[$length - 1] = undef; $self->{length}--; return $element; } method to_array : object[] () { my $length = $self->{length}; my $array = Array->new_proto($self->{values}, $length); Array->memcpy_object_address($array, 0, $self->{values}, 0, $length); return $array; } method unshift : void ($element : object) { my $length = $self->{length}; my $capacity = $self->{capacity}; my $new_length = $length + 1; $self->_maybe_extend($new_length); my $elements = $self->{values}; Array->memmove_object_address($elements, 1, $elements, 0, $length); $elements->[0] = $element; $self->{length}++; } private method _maybe_extend : void ($min_capacity : int) { my $capacity = $self->{capacity}; unless ($min_capacity > $capacity) { return; } if ($capacity < $min_capacity) { $capacity = $min_capacity; } my $new_capacity = $capacity * 2; my $new_values = Array->new_proto($self->{values}, $new_capacity); my $length = $self->{length}; my $elements = $self->{values}; Array->memcpy_object_address($new_values, 0, $elements, 0, $length); $self->{values} = $new_values; $self->{capacity} = $new_capacity; } }