# Copyright (c) 2023 Yuki Kimoto
# MIT License
class Fn {
use StringBuffer;
use StringList;
use IntList;
use Hash;
use Sort;
use List;
use Scope::Guard;
use Callback;
use Format;
use Error::Unicode::InvalidUTF8;
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 "\$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 "\$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 : int*);
precompile static method equals_string_range : int ($string1 : string, $string1_offset : int, $string2 : string, $string2_offset : int, $length : int) {
unless ($string1) {
die "\$string1 must be defined.";
}
unless ($string2) {
die "\$string2 must be defined.";
}
unless ($string1_offset >= 0) {
die "\$string1_offset must be greater than or equal to 0.";
}
unless ($string2_offset >= 0) {
die "\$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 "\$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 "\$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 "\$string must be defined.";
}
unless ($substring) {
die "\$substring must be defined.";
}
unless ($begin >= 0 && $begin <= length $string) {
die "\$begin must be between 0 and the length of \$string.";
}
my $string_length = length $string;
if ($end < 0) {
$end = $string_length;
}
unless ($end <= length $string) {
die "\$end must be less than or equal to the length of \$string.";
}
unless ($end >= $begin) {
die "\$end must be greater than or equal to \$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 "\$string must be defined.";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "\$offset + \$length must be less than or equal to the length of \$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 "\$separator must be defined.";
}
unless ($strings) {
die "\$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 "\$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 "\$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 "\$exponant number must be greater than or equal to 0.";
}
if ($base == 0) {
unless ($exponant != 0) {
die "If \$base number is 0, \$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 "\$exponant number must be greater than or equal to 0.";
}
if ($base == 0) {
unless ($exponant != 0) {
die "If \$base number is 0, \$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) {
# 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 "\$string must be defined.";
}
unless ($count >= 0) {
die "\$repeat count must be greater than or equal to 0.";
}
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 "\$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 "\$string must be defined.";
}
unless ($substring) {
die "\$substring must be defined.";
}
unless ($begin >= 0 && $begin <= length $string) {
die "\$begin must be between 0 and the length of \$string.";
}
my $string_length = length $string;
if ($end < 0) {
$end = $string_length;
}
unless ($end <= length $string) {
die "\$end must be less than or equal to the length of \$string.";
}
unless ($end >= $begin) {
die "\$end must be greater than or equal to \$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 "\$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 "\$separator must be defined.";
}
unless ($string) {
die "\$string must be defined.";
}
my $string_length = length $string;
my $separator_length = length $separator;
unless ($separator_length > 0) {
die "The length of \$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 "\$string must be defined.";
}
unless ($offset >= 0) {
die "\$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 "\$offset + \$length must be less than or equal to the length of \$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 "\$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 "\$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 "\$string must be defined.";
}
unless ($pattern) {
die "\$pattern must be defined.";
}
unless ($replace) {
die "\$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 "\$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 "\$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 "\$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 "\$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 "\$string must be defined.";
}
unless ($utf8_offset >= 0) {
die "\$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 "\$utf8_offset + \$utf8_length must be less than or equal to the UTF-8 length of \$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 $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 $arg_name must be 1 or 3 characters.";
}
}
unless ($min_code_point <= $max_code_point) {
die "The code point of the ending character in $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 \$options1 must be an even number.";
}
my $options2_length = @$options2;
unless ($options2_length % 2 == 0) {
die "The length of \$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 "\$string must be defined.";
}
unless ($offset >= 0) {
die "\$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 "\$offset + \$length must be less than or equal to the length of \$string.";
}
for (my $i = 0; $i < $length; $i++) {
$string->[$offset + $i] = (byte)$char;
}
}
}