# Copyright (c) 2023 Yuki Kimoto
# MIT License
class Fn {
version_from SPVM;
use StringBuffer;
use StringList;
use IntList;
use Hash;
use Sort;
use List;
use Scope::Guard;
use Callback;
use Format;
use Error::Unicode::InvalidUTF8;
use Callback::Grep;
use Callback::Map;
use Callback::MapExpand;
use Cloneable;
use Packer;
# Do not use Native and Native::XXX classes because dependencies can have serious negative effects.
static method BYTE_MAX : int () { return 127; }
static method BYTE_MIN : int () { return -128; }
native static method DBL_MAX : double ();
native static method DBL_MIN : double ();
native static method DOUBLE_MAX : double ();
native static method DOUBLE_MIN : double ();
native static method FLOAT_MAX : float ();
native static method FLOAT_MIN : float();
native static method FLT_MAX : float ();
native static method FLT_MIN : float();
static method INT16_MAX : int () { return 32767; }
static method INT16_MIN : int () { return -32768; }
static method INT32_MAX : int () { return 2147483647; }
static method INT32_MIN : int () { return -2147483648; }
static method INT64_MAX : long () { return 9223372036854775807L; }
static method INT64_MIN : long () { return -9223372036854775808L; }
static method INT8_MAX : int () { return 127; }
static method INT8_MIN : int () { return -128; }
static method INT_MAX : int () { return 2147483647; }
static method INT_MIN : int () { return -2147483648; }
static method LONG_MAX : long () { return 9223372036854775807L; }
static method LONG_MIN : long () { return -9223372036854775808L; }
static method RAND_MAX : int () { return 2147483647; }
static method SHORT_MAX : int () { return 32767; }
static method SHORT_MIN : int () { return -32768; }
static method UBYTE_MAX : int () { return (byte)0xFF; }
static method UINT16_MAX : int () { return (short)0xFFFF; }
static method UINT32_MAX : int () { return 0xFFFFFFFF; }
static method UINT64_MAX : long () { return 0xFFFFFFFFFFFFFFFFL; }
static method UINT8_MAX : int () { return (byte)0xFF; }
static method UINT_MAX : int () { return 0xFFFFFFFF; }
static method ULONG_MAX : long () { return 0xFFFFFFFFFFFFFFFFL; }
static method USHORT_MAX : int () { return (short)0xFFFF; }
static method abs : int ($value : int) {
my $abs = 0;
if ($value > 0) {
$abs = $value;
}
else {
$abs = -$value;
}
return $abs;
}
static method chomp : void ($string : mutable string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $length = length $string;
if ($length >= 2 && $string->[$length - 2] == '\r' && $string->[$length - 1] == '\n') {
&shorten($string, $length - 2);
}
elsif ($length >= 1 && $string->[$length - 1] == '\n') {
&shorten($string, $length - 1);
}
}
static method chompr : string ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $new_string = (mutable string)copy $string;
&chomp($new_string);
return $new_string;
}
static method chr : string ($code_point : int) {
my $is_unicode_scalar_value = &is_unicode_scalar_value($code_point);
my $utf8_char = (string)undef;
if ($is_unicode_scalar_value) {
$utf8_char = &_chr_native($code_point);
}
return $utf8_char;
}
static method contains : int ($string : string, $substring : string, $string_offset : int = 0, $string_length : int = -1) {
return &index($string, $substring, $string_offset, $string_length) >= 0;
}
static method copy_string : string ($string : string) {
return copy $string;
}
native static method crand : int ($seed_ref : int*);
precompile static method equals_string_range : int ($string1 : string, $string1_offset : int, $string2 : string, $string2_offset : int, $length : int) {
unless ($string1) {
die "The string1 \$string1 must be defined.";
}
unless ($string2) {
die "The string2 \$string2 must be defined.";
}
unless ($string1_offset >= 0) {
die "The string1 offset \$string1_offset must be greater than or equal to 0.";
}
unless ($string2_offset >= 0) {
die "The string2 offset \$string2_offset must be greater than or equal to 0.";
}
my $string1_length = length $string1;
unless ($string1_offset + $length <= $string1_length) {
return 0;
}
my $string2_length = length $string2;
unless ($string1_offset + $length <= $string2_length) {
return 0;
}
my $match = 1;
for (my $i = 0; $i < $length; $i++) {
my $char1 = $string1->[$string1_offset + $i];
my $char2 = $string2->[$string2_offset + $i];
unless ($char1 == $char2) {
$match = 0;
last;
}
}
return $match;
}
native static method get_code_point : int ($string : string, $offset_ref : int*);
precompile static method hex : int ($hex_string : string) {
unless ($hex_string) {
die "The hex string \$hex_string must be defined.";
}
my $hex_value = 0;
my $digit = 0;
my $value = 0;
my $length = length $hex_string;
unless ($length >= 1 && $length <= 8) {
die "The length of \$hex string must be 1 to 8.";
}
for (my $i = $length - 1; $i >= 0; $i--) {
my $ascii_code = $hex_string->[$i];
unless (&is_hex_digit($ascii_code)) {
die "The hex string \$hex_string must contain only hex characters.";
}
my $digit_value = 0;
if ($ascii_code >= '0' && $ascii_code <= '9') {
$digit_value = $ascii_code - 48;
}
elsif ($ascii_code >= 'a' && $ascii_code <= 'f') {
$digit_value = $ascii_code - 87;
}
elsif ($ascii_code >= 'A' && $ascii_code <= 'F') {
$digit_value = $ascii_code - 55;
}
$value += $digit_value * &powi(16, $digit);
$digit += 1;
}
return $value;
}
precompile static method index : int ($string : string, $substring : string, $begin : int = 0, $end : int = -1) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($substring) {
die "The substring \$substring must be defined.";
}
unless ($begin >= 0 && $begin <= length $string) {
die "The begin \$begin must be between 0 and the length of the string \$string.";
}
my $string_length = length $string;
if ($end < 0) {
$end = $string_length;
}
unless ($end <= length $string) {
die "The end \$end must be less than or equal to the length of the string \$string.";
}
unless ($end >= $begin) {
die "The end \$end must be greater than or equal to the begin \$begin.";
}
my $substring_length = length $substring;
if ($substring_length == 0) {
return $begin;
}
if ($end == $string_length) {
$end--;
}
for (my $i = $begin; $i <= $end; $i++) {
my $match = 1;
for (my $k = 0; $k < $substring_length; $k++) {
if ($i + $k > $end) {
$match = 0;
last;
}
unless ($string->[$i + $k] == $substring->[$k]) {
$match = 0;
last;
}
}
if ($match) {
return $i;
}
}
return -1;
}
precompile static method init_string : void ($string : mutable string, $ascii_code : int = 0, $offset : int = 0, $length : int = -1) {
unless ($string) {
die "The string \$string must be defined.";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "The offset \$offset + the length \$length must be less than or equal to the length of the string \$string.";
}
for (my $i = $offset; $i < $offset + $length; $i++) {
$string->[$i] = (byte)$ascii_code;
}
}
static method is_alnum : int ($code_point : int) {
if (($code_point >= 'A' && $code_point <= 'Z') || ($code_point >= 'a' && $code_point <= 'z') || ($code_point >= '0' && $code_point <= '9')) {
return 1;
}
else {
return 0;
}
}
static method is_alpha : int ($code_point : int) {
if (($code_point >= 'A' && $code_point <= 'Z') || ($code_point >= 'a' && $code_point <= 'z')) {
return 1;
}
else {
return 0;
}
}
native static method is_array : int ($object : object);
static method is_blank : int ($code_point : int) {
# SP or HT
if ($code_point >= '\x20' || $code_point <= '\x9') {
return 1;
}
else {
return 0;
}
}
native static method is_class : int ($object : object);
static method is_cntrl : int ($code_point : int) {
if (($code_point >= 0x00 && $code_point <= 0x1f) || $code_point == 0x7f) {
return 1;
}
else {
return 0;
}
}
static method is_digit : int ($code_point : int) {
if ($code_point >= '0' && $code_point <= '9') {
return 1;
}
else {
return 0;
}
}
static method is_graph : int ($code_point : int) {
if ($code_point >= 0x21 && $code_point <= 0x7E) {
return 1;
}
else {
return 0;
}
}
static method is_hex_digit : int ($code_point : int) {
if (($code_point >= '0' && $code_point <= '9') || ($code_point >= 'a' && $code_point <= 'f') || ($code_point >= 'A' && $code_point <= 'F')) {
return 1;
}
else {
return 0;
}
}
static method is_lower : int ($code_point : int) {
if ($code_point >= 'a' && $code_point <= 'z') {
return 1;
}
else {
return 0;
}
}
native static method is_mulnum_array : int ($object : object);
native static method is_numeric_array : int ($object : object);
native static method is_object_array : int ($object : object);
# This is same as Perl ASCII mode \s
static method is_perl_space : int ($code_point : int) {
my $is_perl_space = 0;
switch ($code_point) {
case 0x20: # ' ' SP
case 0x0D: # '\r' CR
case 0x0A: # '\n' LF
case 0x09: # '\t' HT
case 0x0C: # '\f' FF
{
$is_perl_space = 1;
break;
}
}
return $is_perl_space;
}
static method is_perl_word : int ($code_point : int) {
my $ispword = 0;
if ($code_point >= 'a' && $code_point <= 'z') {
$ispword = 1;
}
elsif ($code_point >= 'A' && $code_point <= 'Z') {
$ispword = 1;
}
elsif ($code_point == '_') {
$ispword = 1;
}
elsif ($code_point >= '0' && $code_point <= '9') {
$ispword = 1;
}
return $ispword;
}
native static method is_pointer_class : int ($object : object);
static method is_print : int ($code_point : int) {
if ($code_point >= 0x20 && $code_point <= 0x7E) {
return 1;
}
else {
return 0;
}
}
static method is_punct : int ($code_point : int) {
if (($code_point >= 0x21 && $code_point <= 0x2F) || ($code_point >= 0x3A && $code_point <= 0x40) || ($code_point >= 0x5B && $code_point <= 0x60) || ($code_point >= 0x7B && $code_point <= 0x7E)) {
return 1;
}
else {
return 0;
}
}
static method is_space : int ($code_point : int) {
if (($code_point >= 0x09 && $code_point <= 0x0D) || $code_point == 0x20) {
return 1;
}
else {
return 0;
}
}
private static method is_unicode_scalar_value : int ($code_point: int) {
my $is_unicode_scalar_value = 0;
# The range of Unicde code points
if ($code_point >= 0 && $code_point <= 0x10FFFF) {
# Not surrogate code points
unless ($code_point >= 0xD800 && $code_point <= 0xDFFF) {
$is_unicode_scalar_value = 1;
}
}
return $is_unicode_scalar_value;
}
static method is_upper : int ($code_point : int) {
if ($code_point >= 'A' && $code_point <= 'Z') {
return 1;
}
else {
return 0;
}
}
static method is_xdigit : int ($code_point : int) {
if (($code_point >= 'A' && $code_point <= 'F') || ($code_point >= 'a' && $code_point <= 'f') || ($code_point >= '0' && $code_point <= '9')) {
return 1;
}
else {
return 0;
}
}
precompile static method join : string ($separator : string, $strings : string[]) {
unless ($separator) {
die "The separator \$separator must be defined.";
}
unless ($strings) {
die "The strings \$strings must be defined.";
}
my $join_buffer = StringBuffer->new;
for (my $i = 0; $i < @$strings; $i++) {
my $string = $strings->[$i];
if ($string) {
$join_buffer->push($string);
}
else {
$join_buffer->push("");
}
if ($i != @$strings - 1) {
$join_buffer->push($separator);
}
}
my $join = $join_buffer->to_string;
return $join;
}
static method labs : long ($value : long) {
my $labs = 0L;
if ($value > 0) {
$labs = $value;
}
else {
$labs = -$value;
}
return $labs;
}
precompile static method lc : string ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
for (my $i = 0; $i < $length; $i++) {
my $char = $string->[$i];
if ($char >= 'A' && $char <= 'Z') {
$new_string->[$i] = (byte)($string->[$i] + 32);
}
else {
$new_string->[$i] = $string->[$i];
}
}
return $new_string;
}
static method lcfirst : string ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
if ($length > 0) {
my $char = $string->[0];
if ($char >= 'A' && $char <= 'Z') {
$new_string->[0] = (byte)($char + 32);
}
else {
$new_string->[0] = $char;
}
}
Fn->memcpy($new_string, 1, $string, 1, $length - 1);
return $new_string;
}
static method look_code_point : int ($string : string, $offset_ref : int*) {
my $save_offset = $$offset_ref;
my $code_point = &get_code_point($string, $offset_ref);
$$offset_ref = $save_offset;
return $code_point;
}
native static method memcpy : void ($dest : object, $dest_offset : int, $source : object, $source_offset : int, $length : int);
native static method memmove : void ($dest : object, $dest_offset : int, $source : object, $source_offset : int, $length : int);
static method ord : int ($string : string) {
my $offset = 0;
my $code_point = &get_code_point($string, \$offset);
return $code_point;
}
precompile static method powi : int ($base : int, $exponant : int) {
unless ($exponant >= 0) {
die "The exponent number \$exponant must be greater than or equal to 0.";
}
if ($base == 0) {
unless ($exponant != 0) {
die "If the base number \$base is 0, the exponent number \$exponant number cannnot be 0.";
}
}
my $ret = 1;
for (my $i = 0; $i < $exponant; $i++) {
$ret = $ret * $base;
}
return $ret;
}
precompile static method powl : long ($base : long, $exponant : long) {
unless ($exponant >= 0) {
die "The exponent number \$exponant number must be greater than or equal to 0.";
}
if ($base == 0) {
unless ($exponant != 0) {
die "If the base number \$base number is 0, the exponent number \$exponant number cannnot be 0.";
}
}
my $ret = 1L;
for (my $i = 0; $i < $exponant; $i++) {
$ret = $ret * $base;
}
return $ret;
}
static method rand : double ($seed : int*, $max : int = 1) {
unless ($max > 0) {
die "The max number \$max must be greater than 0.";
}
# 0 <= random_number < 1
my $random_number = (double)&crand($seed) / ((double)&RAND_MAX() + 1);
$random_number *= $max;
return $random_number;
}
precompile static method repeat : string ($string : string, $count : int) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($count >= 0) {
die "The repeat count \$count must be a non-negative integer.";
}
my $buffer = StringBuffer->new;
for (my $i = 0; $i < $count; $i++) {
$buffer->push($string);
}
my $repeat_string = $buffer->to_string;
return $repeat_string;
}
precompile static method replace_chars : void ($string : mutable string, $from_ch : int, $to_ch : int) {
unless ($string) {
die "The string \$string must be defined.";
}
my $string_length = length $string;
for (my $i = 0; $i < $string_length; $i++) {
if ($string->[$i] == $from_ch) {
$string->[$i] = (byte)$to_ch;
}
}
}
precompile static method rindex : int ($string : string, $substring : string, $end : int = -1, $begin : int = 0) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($substring) {
die "The substring \$substring must be defined.";
}
unless ($begin >= 0 && $begin <= length $string) {
die "The begin \$begin must be between 0 and the length of the string \$string.";
}
my $string_length = length $string;
if ($end < 0) {
$end = $string_length;
}
unless ($end <= length $string) {
die "The end \$end must be less than or equal to the length of the string \$string.";
}
unless ($end >= $begin) {
die "The end \$end must be greater than or equal to the begin \$begin.";
}
my $substring_length = length $substring;
if ($substring_length == 0) {
return $end;
}
if ($end == $string_length) {
$end--;
}
my $match_chars_count = 0;
for (my $i = $end; $i >= $begin; $i--) {
my $match = 1;
for (my $k = 0; $k < $substring_length; $k++) {
if ($i + $k > $end) {
$match = 0;
last;
}
unless ($string->[$i + $k] == $substring->[$k]) {
$match = 0;
last;
}
}
if ($match) {
return $i;
}
}
return -1;
}
native static method sizeof_native_int : int ();
native static method sizeof_native_pointer : int ();
native static method shorten : void ($string : mutable string, $length : int);
precompile static method shorten_null_char : void ($string : mutable string) {
if (!$string) {
die "The string \$string must be defined.";
}
my $null_char_offset = -1;
for (my $i = 0; $i < length $string; $i++) {
my $char = $string->[$i];
if ($char == '\0') {
$null_char_offset = $i;
last;
}
}
if ($null_char_offset >= 0) {
Fn->shorten($string, $null_char_offset);
}
}
precompile static method split : string[] ($separator : string, $string : string, $limit : int = 0) {
unless ($separator) {
die "The separator \$separator must be defined.";
}
unless ($string) {
die "The string \$string must be defined.";
}
my $string_length = length $string;
my $separator_length = length $separator;
unless ($separator_length > 0) {
die "The length of the separator \$separator must be greater than 0.";
}
my $parts_list = StringList->new_len(0);
my $offset = 0;
my $match_count = 0;
for (my $i = 0; $i < $string_length; $i++) {
if ($limit > 0 && $match_count >= $limit - 1) {
last;
}
my $match_offset = &index($string, $separator, $offset);
my $match = $match_offset >= 0;
if ($match) {
$match_count++;
my $part = &substr($string, $offset, $match_offset - $offset);
$parts_list->push($part);
my $match_legnth = $separator_length;
$offset = $match_offset + $match_legnth;
}
}
if ($offset == $string_length) {
$parts_list->push("");
}
else {
my $part = &substr($string, $offset, $string_length - $offset);
$parts_list->push($part);
}
if ($limit == 0) {
while ($parts_list->length > 0) {
if ($parts_list->get($parts_list->length - 1) eq "") {
$parts_list->pop;
}
else {
last;
}
}
}
my $parts = $parts_list->to_array;
return $parts;
}
static method substr : string ($string : string, $offset : int, $length : int = -1, $replacement : string = undef) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($offset >= 0) {
die "The offset \$offset must be greater than or equal to 0.";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "The offset \$offset + the length \$length must be less than or equal to the length of the string \$string.";
}
my $substring = (string)undef;
if ($replacement) {
my $replacement_length = length $replacement;
$substring = Fn->substr($string, 0, $offset) . $replacement . Fn->substr($string, $offset + $length);
}
else {
$substring = (mutable string)new_string_len($length);
Fn->memcpy($substring, 0, $string, $offset, $length);
}
return $substring;
}
precompile static method to_code_points : int[] ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $string_length = length $string;
my $offset = 0;
my $utf8_length = 0;
my $code_points_list = IntList->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
$code_points_list->push($code_point);
}
my $code_points = $code_points_list->to_array;
return $code_points;
}
native static method to_double : double ($string : string);
native static method to_float : float ($string : string);
static method to_int : int ($string : string) {
return &to_int_with_base($string, 10);
}
native static method to_int_with_base : int ($string : string, $digit : int);
static method to_long : long ($string : string) {
return &to_long_with_base($string, 10);
}
native static method to_long_with_base : long ($string : string, $digit : int);
static method to_lower : int ($code_point : int) {
if ($code_point >= 'A' && $code_point <= 'Z') {
$code_point = $code_point + 0x20;
}
return $code_point;
}
static method to_upper : int ($code_point : int) {
if ($code_point >= 'a' && $code_point <= 'z') {
$code_point = $code_point - 0x20;
}
return $code_point;
}
precompile static method to_utf8_chars : string[] ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $string_length = length $string;
my $offset = 0;
my $utf8_length = 0;
my $utf8_chars_list = StringList->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
my $utf8_char = &chr($code_point);
$utf8_chars_list->push($utf8_char);
}
my $utf8_chars = $utf8_chars_list->to_array;
return $utf8_chars;
}
precompile static method tr : string ($string : string, $pattern : string, $replace : string) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($pattern) {
die "The pattern \$pattern must be defined.";
}
unless ($replace) {
die "The replace \$replace must be defined.";
}
my $replace_length = length $pattern;
my $pattern_range = &_parse_range($pattern, "$pattern");
my $replace_range = &_parse_range($replace, "$replace");
my $string_length = length $string;
my $offset = 0;
my $buffer = StringBuffer->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
my $pattern_offset = 0;
my $match = 0;
my $replace_pos = 0;
my $match_pattern_index = -1;
my $match_code_point_offset = -1;
my $min_code_point = $pattern_range->[0];
my $max_code_point = $pattern_range->[1];
if ($code_point >= $min_code_point && $code_point <= $max_code_point) {
$match = 1;
$match_code_point_offset = $code_point - $min_code_point;
}
if ($match) {
my $replace_code_point = $replace_range->[0] + $match_code_point_offset;
my $char = &chr($replace_code_point);
$buffer->push($char);
}
else {
my $char = &chr($code_point);
$buffer->push($char);
}
}
my $ret = $buffer->to_string;
return $ret;
}
precompile static method trim : string ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $length = length $string;
my $begin_index = -1;
my $end_index = -1;
for (my $i = 0; $i < $length; $i++) {
if ($begin_index == -1) {
if (&is_space($string->[$i])) {
# Skip
}
else {
$begin_index = $i;
last;
}
}
}
for (my $i = $length - 1; $i >= 0; $i--) {
if ($end_index == -1) {
if (&is_space($string->[$i])) {
# Skip
}
else {
$end_index = $i;
last;
}
}
}
my $trimed_string : string;
if ($begin_index == -1 && $end_index == -1) {
return "";
}
elsif ($end_index == -1) {
$trimed_string = &substr($string, $begin_index, $length - $begin_index);
}
elsif ($end_index == -1) {
$trimed_string = &substr($string, 0, $end_index + 1);
}
else {
$trimed_string = &substr($string, $begin_index, $end_index - $begin_index + 1);
}
return $trimed_string;
}
precompile static method uc : string ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
for (my $i = 0; $i < $length; $i++) {
my $char = $string->[$i];
if ($char >= 'a' && $char <= 'z') {
$new_string->[$i] = (byte)($string->[$i] - 32);
}
else {
$new_string->[$i] = $string->[$i];
}
}
return $new_string;
}
static method ucfirst : string ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
if ($length > 0) {
my $char = $string->[0];
if ($char >= 'a' && $char <= 'z') {
$new_string->[0] = (byte)($char - 32);
}
else {
$new_string->[0] = $char;
}
}
Fn->memcpy($new_string, 1, $string, 1, $length - 1);
return $new_string;
}
precompile static method utf8_length : int ($string : string) {
unless ($string) {
die "The string \$string must be defined.";
}
my $string_length = length $string;
my $offset = 0;
my $utf8_length = 0;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
$utf8_length++;
}
return $utf8_length;
}
precompile static method utf8_substr : string ($string : string, $utf8_offset : int, $utf8_length : int = -1) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($utf8_offset >= 0) {
die "The offset \$utf8_offset must be greater than or equal to 0.";
}
my $string_length = length $string;
my $offset = 0;
my $current_utf8_offset = 0;
my $buffer = StringBuffer->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
# -1:before, 0:range, 1:after
my $range = 0;
if ($current_utf8_offset < $utf8_offset) {
$range = -1;
}
else {
if ($utf8_length < 0) {
$range = 0;
}
else {
if ($current_utf8_offset < $utf8_offset + $utf8_length) {
$range = 0;
}
else {
$range = 1;
}
}
}
if ($range == 0) {
my $utf8_char = &chr($code_point);
$buffer->push($utf8_char);
}
elsif ($range == 1) {
last;
}
$current_utf8_offset++;
}
if ($utf8_length >= 0) {
unless ($utf8_offset + $utf8_length <= $current_utf8_offset) {
die "The offset \$utf8_offset + the length \$utf8_length must be less than or equal to the UTF-8 length of the string \$string.";
}
}
my $substring = $buffer->to_string;
return $substring;
}
static method defer : Scope::Guard ($callback : Callback) {
my $guard = Scope::Guard->new($callback);
return $guard;
}
native static method _chr_native : string ($uchar : int);
private precompile static method _parse_range : int[] ($range_format : string, $arg_name : string) {
my $range = IntList->new;
my $range_format_length = length $range_format;
my $offset = 0;
my $min_code_point = -1;
my $max_code_point = -1;
my $code_points_index = 0;
while ($offset < $range_format_length) {
my $code_point = &get_code_point($range_format, \$offset);
if ($code_points_index == 0) {
$min_code_point = $code_point;
}
elsif ($code_points_index == 1) {
unless ($code_point == '-') {
die "The second character ot the range format of the argument name $arg_name must be \"-\".";
}
}
elsif ($code_points_index == 2) {
$max_code_point = $code_point;
}
$code_points_index++;
}
my $code_points_length = $code_points_index;
unless ($code_points_length == 3) {
if ($code_points_length == 1) {
$max_code_point = $min_code_point;
}
if ($max_code_point < 0) {
die "The range format of the argument name $arg_name must be 1 or 3 characters.";
}
}
unless ($min_code_point <= $max_code_point) {
die "The code point of the ending character in the argument name $arg_name must be greater than or equal to the code point of the begining caharater.";
}
return [$min_code_point, $max_code_point];
}
precompile static method merge_options : object[] ($options1 : object[], $options2 : object[]) {
unless ($options1) {
$options1 = {};
}
unless ($options2) {
$options2 = {};
}
my $options1_length = @$options1;
unless ($options1_length % 2 == 0) {
die "The length of the options1 \$options1 must be an even number.";
}
my $options2_length = @$options2;
unless ($options2_length % 2 == 0) {
die "The length of the options2 \$options2 must be an even number.";
}
my $merged_options = Array->merge_object($options1, $options2);
return $merged_options;
}
native static method object_to_int : int ($object : object);
native static method object_to_long : long ($object : object);
native static method get_spvm_version_string : string ();
native static method get_spvm_version_number : double ();
native static method get_version_string : string ($basic_type_name : string);
native static method get_version_number : double ($basic_type_name : string);
native static method get_memory_blocks_count : int ();
static method to_address : string ($object : object) {
my $address = Format->sprintf("%p", [$object]);
return $address;
}
static method check_option_names : void ($options : object[], $available_option_names : string[]) {
unless ($options) {
return;
}
my $available_option_names_h = Hash->new;
for my $available_option_name (@$available_option_names) {
$available_option_names_h->set_int($available_option_name, 1);
}
for (my $i = 0; $i < @$options; $i += 2) {
my $option_name = (string)$options->[$i];
unless ($available_option_names_h->get($option_name)) {
die "The \"$option_name\" option is not available.";
}
}
}
native static method get_basic_type_id : int ($basic_type_name : string);
precompile static method memset_char : void ($string : mutable string, $char : int, $offset : int = 0, $length : int = -1) {
unless ($string) {
die "The string \$string must be defined.";
}
unless ($offset >= 0) {
die "The offset \$offset must be greater than or equal to 0.";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "The offset \$offset + the length \$length must be less than or equal to the length of the string \$string.";
}
for (my $i = 0; $i < $length; $i++) {
$string->[$offset + $i] = (byte)$char;
}
}
static method or : object ($left : object, $right : object) {
if ($left) {
return $left;
}
else {
return $right;
}
}
static method if : object ($condition : int, $left : object, $right : object) {
if ($condition) {
return $left;
}
else {
return $right;
}
}
precompile static method grep : object[] ($array : object[], $callback : Callback::Grep) {
unless ($array) {
die "The array \$array must be defined.";
}
unless ($callback) {
die "The callback \$callback must be defined.";
}
my $proto_array = Array->new_proto($array, 0);
my $list = List->new($proto_array);
for my $element (@$array) {
my $ok = $callback->($element);
if ($ok) {
$list->push($element);
}
}
my $new_array = $list->to_array;
return $new_array;
}
precompile static method map : object[] ($array : object[], $callback : Callback::Map) {
unless ($array) {
die "The array \$array must be defined.";
}
unless ($callback) {
die "The callback \$callback must be defined.";
}
my $proto_array = Array->new_proto($array, 0);
my $list = List->new($proto_array);
for my $element (@$array) {
my $new_element = $callback->($element);
$list->push($new_element);
}
my $new_array = $list->to_array;
return $new_array;
}
precompile static method map_expand : object[] ($array : object[], $callback : Callback::MapExpand) {
unless ($array) {
die "The array \$array must be defined.";
}
unless ($callback) {
die "The callback \$callback must be defined.";
}
my $proto_array = Array->new_proto($array, 0);
my $list = List->new($proto_array);
for my $element (@$array) {
my $new_elements = $callback->($element);
for my $new_element (@$new_elements) {
$list->push($new_element);
}
}
my $new_array = $list->to_array;
return $new_array;
}
native static method get_compile_type_name : string ($basic_type_name : string, $type_dimension : int, $type_flag : int);
static method is_any_numeric_array : int ($object : object) {
my $is_numeric_array = &is_numeric_array($object);
my $is_mulnum_array = &is_mulnum_array($object);
my $is_any_numeric_array = $is_numeric_array || $is_mulnum_array;
return $is_any_numeric_array;
}
native static method array_length : int ($array : object);
native static method get_elem_size : int ($array : object);
static method get_elem_type_name : string ($array : object) {
unless ($array) {
die "The array \$array must be defined.";
}
unless (Fn->is_array($array)) {
die "The type of the array \$array must be an array type.";
}
my $type_name = type_name $array;
my $elem_type_name = Fn->substr($type_name, 0, length $type_name - 2);
return $elem_type_name;
}
native static method print_stderr : void ($string : string);
native static method say_stderr : void ($string : string);
native static method memcmp : int ($data1 : object, $data1_offset : int, $data2 : object, $data2_offset : int, $length : int);
static method reverse_inplace : void ($array_or_string : object) {
unless ($array_or_string) {
die "\$array_or_string must be defined.";
}
unless (&is_array($array_or_string ) || $array_or_string is_type string) {
die "The type of \$array_or_string must be an array type or string type.";
}
if ($array_or_string is_type string) {
my $string = (mutable string)$array_or_string;
if (is_read_only $string) {
die "The string \$array_or_string must not be read-only.";
}
my $length = length $string;
for (my $i = 0; $i < $length / 2; $i++) {
my $temp = $string->[$i];
$string->[$i] = $string->[$length - $i - 1];
$string->[$length - $i - 1] = $temp;
}
}
elsif (&is_object_array($array_or_string)) {
my $object_array = (object[])$array_or_string;
my $length = @$object_array;
for (my $i = 0; $i < $length / 2; $i++) {
my $temp = $object_array->[$i];
$object_array->[$i] = $object_array->[$length - $i - 1];
$object_array->[$length - $i - 1] = $temp;
}
}
elsif (&is_any_numeric_array($array_or_string) || $array_or_string is_type string) {
my $array = $array_or_string;
my $length = &array_length($array);
my $elem_size = &get_elem_size($array);
my $temp = new byte[$elem_size];
for (my $i = 0; $i < $length / 2; $i++) {
&memcpy($temp, 0, $array, $elem_size * $i, $elem_size);
&memcpy($array, $elem_size * $i, $array, $elem_size * ($length - $i - 1), $elem_size);
&memcpy($array, $elem_size * ($length - $i - 1), $temp, 0, $elem_size);
}
}
else {
die "[Unpexpected Error]Invalid array type.";
}
}
static method is_string_array : int ($object : object) {
my $is_string_array = 0;
if ($object) {
my $type_name = type_name $object;
if ($type_name eq "string[]") {
$is_string_array = 1;
}
}
return $is_string_array;
}
static method length : int ($array_or_string : object) {
unless ($array_or_string) {
die "\$array_or_string must be defined.";
}
my $length = -1;
if (&is_array($array_or_string)) {
$length = &array_length($array_or_string);
}
elsif ($array_or_string is_type string) {
$length = length (string)$array_or_string;
}
else {
die "The type of \$array_or_string must be an array type or string type.";
}
return $length;
}
static method get_elem_or_char_size : int ($array_or_string : object) {
unless ($array_or_string) {
die "\$array_or_string must be defined.";
}
my $size = -1;
if (&is_array($array_or_string)) {
$size = &get_elem_size($array_or_string);
}
elsif ($array_or_string is_type string) {
$size = 1;
}
else {
die "The type of \$array_or_string must be an array type or string type.";
}
return $size;
}
static method copy : object ($array_or_string : object, $shallow : int = 0) {
unless ($array_or_string) {
return undef;
}
my $copy = (object)undef;
if (&is_string_array($array_or_string)) {
if ($shallow) {
$copy = Array->copy_string_address((string[])$array_or_string);
}
else {
$copy = Array->copy_string((string[])$array_or_string);
}
}
elsif (&is_object_array($array_or_string)) {
if ($shallow) {
$copy = Array->copy_object_address((object[])$array_or_string);
}
else {
$copy = Array->copy_object((object[])$array_or_string, Cloner->default_cloner);
}
}
elsif (&is_any_numeric_array($array_or_string)) {
$copy = Array->copy_any_numeric($array_or_string);
}
elsif ($array_or_string is_type string) {
$copy = copy (string)$array_or_string;
}
else {
die "The type of \$array_or_string must be an array type or string type.";
}
return $copy;
}
static method reverse : object ($array_or_string : object) {
my $ret = Fn->copy($array_or_string);
&reverse_inplace($ret);
return $ret;
}
static method slice : object ($array : object, $offset : int, $length :int) {
unless ($array) {
die "The array \$array must be defined.";
}
unless (Fn->is_any_numeric_array($array) || Fn->is_object_array($array)) {
die "The type of the array \$array must be an object array type, a numeric array type or a multi-numeric array type.";
}
my $sliced_array = Array->new_proto_any($array, $length);
if (&is_object_array($array)) {
Array->memcpy_object_address((object[])$sliced_array, 0, (object[])$array, $offset, $length);
}
elsif (&is_any_numeric_array($array)) {
my $elem_size = Fn->get_elem_size($array);
Fn->memcpy($sliced_array, 0, $array, $elem_size * $offset, $elem_size * $length);
}
else {
die "[Unexpected Error]Invalid type";
}
return $sliced_array;
}
native static method system_is_little_endian : int ();
static method sprintf : string ($format : string, $args : object[]) {
return Format->sprintf($format, $args);
}
static method sort_asc : object ($array : object) {
unless ($array) {
die "The array \$array must be defined.";
}
unless (Fn->is_numeric_array($array)) {
die "The type of the array \$array must be a numeric array type.";
}
my $sorted_array = Fn->copy($array);
if ($array is_type byte[]) {
Sort->sort_byte_asc((byte[])$sorted_array);
}
elsif ($array is_type short[]) {
Sort->sort_short_asc((short[])$sorted_array);
}
elsif ($array is_type int[]) {
Sort->sort_int_asc((int[])$sorted_array);
}
elsif ($array is_type long[]) {
Sort->sort_long_asc((long[])$sorted_array);
}
elsif ($array is_type float[]) {
Sort->sort_float_asc((float[])$sorted_array);
}
elsif ($array is_type double[]) {
Sort->sort_double_asc((double[])$sorted_array);
}
return $sorted_array;
}
static method sort_desc : object ($array : object) {
unless ($array) {
die "The array \$array must be defined.";
}
unless (Fn->is_numeric_array($array)) {
die "The type of the array \$array must be a numeric array type.";
}
my $sorted_array = Fn->copy($array);
if ($array is_type byte[]) {
Sort->sort_byte_desc((byte[])$sorted_array);
}
elsif ($array is_type short[]) {
Sort->sort_short_desc((short[])$sorted_array);
}
elsif ($array is_type int[]) {
Sort->sort_int_desc((int[])$sorted_array);
}
elsif ($array is_type long[]) {
Sort->sort_long_desc((long[])$sorted_array);
}
elsif ($array is_type float[]) {
Sort->sort_float_desc((float[])$sorted_array);
}
elsif ($array is_type double[]) {
Sort->sort_double_desc((double[])$sorted_array);
}
return $sorted_array;
}
static method sort : object[] ($array : object[], $comparator : Comparator) {
my $sorted_array = Array->copy_object_address($array);
Sort->sort_object($array, $comparator);
return $sorted_array;
}
static method change_endian : void ($binary : mutable string, $size : int, $offset : int = 0) {
unless ($binary) {
die "The binary data \$binary must be defined.";
}
unless ($size > 0) {
die "The byte size \$size must be greater than 0.";
}
unless ($offset >= 0) {
die "The offset \$offset must be greater than or equal to 0.";
}
my $binary_length = length $binary;
unless ($offset + $size <= $binary_length) {
die "The byte size \$size must be less than or equal to the offset \$offset plus the length of the binary data \$binary.";
}
if ($size == 1) {
return;
}
my $buffer = new_string_len $size;
Fn->memcpy($buffer, 0, $binary, $offset, $size);
Fn->reverse_inplace($buffer);
Fn->memcpy($binary, $offset, $buffer, 0, $size);
}
static method big_endian_to_system_endian : void ($binary : mutable string, $size : int, $offset : int = 0) {
if (Fn->system_is_little_endian) {
&change_endian($binary, $size, $offset);
}
}
static method system_endian_to_big_endian : void ($binary : mutable string, $size : int, $offset : int = 0) {
if (Fn->system_is_little_endian) {
&change_endian($binary, $size, $offset);
}
}
static method little_endian_to_system_endian : void ($binary : mutable string, $size : int, $offset : int = 0) {
unless (Fn->system_is_little_endian) {
&change_endian($binary, $size, $offset);
}
}
static method system_endian_to_little_endian : void ($binary : mutable string, $size : int, $offset : int = 0) {
unless (Fn->system_is_little_endian) {
&change_endian($binary, $size, $offset);
}
}
static method pack : string ($template : string, $objects : object[]) {
return Packer->new->pack($template, $objects);
}
static method unpack : object[] ($template : string, $binary : string) {
return Packer->new->unpack($template, $binary);
}
native static method no_free : int ($object : object);
native static method set_no_free : void ($object : object, $no_free : int);
native static method get_pointer : Address ($object : object);
native static method set_pointer : void ($object : object, $address : Address);
native static method has_null_pointer : int ($object : object);
native static method eq_pointer : int ($object1 : object, $object2 : object);
native static method pointer_to_string : string ($object : object);
native static method dump_object_internal : string ($object : object);
native static method get_seed : int ();
native static method set_seed : void ($seed : int);
native static method seed_initialized : int ();
native static method get_basic_type_name_in_version_from : string ($basic_type_name : string);
}