# 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;
}
}