# Copyright (c) 2023 Yuki Kimoto # MIT License class StringBuffer { use Fn; use Array; # Enumerations enum { DEFAULT_CAPACITY = 4, } # Fields has capacity : ro int; has length : ro int; has value : ro mutable string; # Class methods static method new : StringBuffer ($string = undef : string, $capacity = -1 : int) { my $length : int; if ($string) { $length = length $string; } else { $length = 0; } my $self = &new_len($length, $capacity); if ($string) { Fn->memcpy($self->{value}, 0, $string, 0, $length); } return $self; } static method new_len : StringBuffer ($length : int, $capacity = -1 : int) { 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; } my $self = new StringBuffer; $self->{value} = (mutable string)new_string_len($capacity); $self->{capacity} = $capacity; $self->{length} = $length; return $self; } # Instance methods method push : void ($string : string, $offset = 0 : int, $length = -1 : int) { unless ($string) { die "The \$string must be defined"; } my $string_length = length $string; unless ($offset >= 0) { die "The \$offset must be greater than or equal to 0"; } if ($length == -1) { $length = length $string - $offset; } unless ($offset + $length <= $string_length) { die "The \$offset + \$length must be less than or equal to the length of the \$string"; } my $buffer_length = $self->{length}; my $new_length = $buffer_length + $length; $self->_maybe_extend($new_length); Fn->memcpy($self->{value}, $buffer_length, $string, $offset, $length); $self->{length} += $length; } method push_char : void ($char : int) { my $length = $self->{length}; my $new_length = $length + 1; $self->_maybe_extend($new_length); $self->{value}[$self->{length}++] = (byte)$char; } method replace : void ($offset : int, $remove_length : int, $replace : string) { 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 \$string buffer"); } my $replace_length = 0; if ($replace) { $replace_length = length $replace; } my $new_length = $self->{length} - $remove_length + $replace_length; $self->_maybe_extend($new_length); my $move_length = $self->{length} - $offset - $remove_length; Fn->memmove($self->{value}, $offset + $replace_length, $self->{value}, $offset + $remove_length, $move_length); if ($replace) { Fn->memcpy($self->{value}, $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_value = (mutable string)new_string_len $new_capacity; Fn->memcpy($new_value, 0, $self->{value}, 0, $length); $self->{value} = $new_value; $self->{capacity} = $new_capacity; } } method to_string : string () { my $string = Fn->substr($self->{value}, 0, $self->{length}); return $string; } 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_value = (mutable string)new_string_len $new_capacity; my $length = $self->{length}; my $value = $self->{value}; Fn->memcpy($new_value, 0, $value, 0, $length); $self->{value} = $new_value; $self->{capacity} = $new_capacity; } }