github.com/remobjects/goldbaselibrary@v0.0.0-20230924164425-d458680a936b/Source/Gold/string.pas (about)

     1  namespace go.builtin;
     2  
     3  uses
     4  {$IFDEF ECHOES}  System.Text, System.Linq, System.Collections.Generic{$ENDIF}
     5    ;
     6  
     7  type
     8    string = public partial record
     9    public
    10      Value: Slice<byte>;
    11  
    12      constructor(aValue: PlatformString);
    13      begin
    14        {$IF ISLAND}
    15        Value := Encoding.UTF8.GetBytes(aValue);
    16        {$ELSEIF ECHOES}
    17        Value := System.Text.Encoding.UTF8.GetBytes(aValue);
    18        {$ENDIF}
    19      end;
    20  
    21      constructor;
    22      begin
    23        Value := new Slice<byte>(0);
    24      end;
    25  
    26      constructor(aValue: array of Char);
    27      begin
    28        {$IF ECHOES}
    29        Value := Encoding.UTF8.GetBytes(aValue);
    30        {$ELSE}
    31        var lString := PlatformString.FromCharArray(aValue);
    32        Value := Encoding.UTF8.GetBytes(lString);
    33        {$ENDIF}
    34      end;
    35  
    36      constructor(aValue: Slice<byte>);
    37      begin
    38        Value := new Slice<byte>(aValue.Length);
    39        copy(Value, aValue);
    40      end;
    41  
    42      constructor(aValue: array of rune);
    43      begin
    44        var lArray := new Char[aValue.Length];
    45        for i: Integer := 0 to aValue.Length - 1 do
    46          lArray[i] := chr(aValue[i]);
    47  
    48        constructor(lArray);
    49      end;
    50  
    51      class operator Implicit(aValue: string): PlatformString;
    52      begin
    53        {$IF ISLAND}
    54        result := Encoding.UTF8.GetString(aValue.Value);
    55        {$ELSEIF ECHOES}
    56        result := System.Text.Encoding.UTF8.GetString(aValue.Value);
    57        {$ENDIF}
    58      end;
    59  
    60      class operator Implicit(aValue: PlatformString): string;
    61      begin
    62        {$IF ISLAND}
    63        result := new string(Encoding.UTF8.GetBytes(aValue));
    64        {$ELSEIF ECHOES}
    65        result := new string(System.Text.Encoding.UTF8.GetBytes(aValue));
    66        {$ENDIF}
    67      end;
    68  
    69      class operator Implicit(aValue: array of uint8): string;
    70      begin
    71        result := new string(aValue);
    72      end;
    73  
    74      class operator Implicit(aValue: string): Slice<byte>;
    75      begin
    76        if aValue.Value ≠ nil then begin
    77          var lData := aValue.Value.ToArray;
    78          result := new Slice<byte>(lData);
    79        end
    80        else
    81          result := new Slice<byte>(0);
    82      end;
    83  
    84      class operator Implicit(aValue: Char): string;
    85      begin
    86        result := new string([aValue]);
    87      end;
    88  
    89      class operator Equal(a, b: string): Boolean;
    90      begin
    91        result := go.bytes.Compare(a.Value, b.Value) = 0;
    92      end;
    93  
    94      class operator NotEqual(a, b: string): Boolean;
    95      begin
    96        result := not (a = b);
    97      end;
    98  
    99      class operator Less(aLeft, aRight: string): Boolean;
   100      begin
   101        result := go.bytes.Compare(aLeft.Value, aRight.Value) < 0;
   102      end;
   103  
   104      class operator LessOrEqual(aLeft, aRight: string): Boolean;
   105      begin
   106        result := go.bytes.Compare(aLeft.Value, aRight.Value) <= 0;
   107      end;
   108  
   109      class operator GreaterOrEqual(aLeft, aRight: string): Boolean;
   110      begin
   111        result := go.bytes.Compare(aLeft.Value, aRight.Value) ≥ 0;
   112      end;
   113  
   114      class operator Greater(aLeft, aRight: string): Boolean;
   115      begin
   116        result := go.bytes.Compare(aLeft, aRight) > 0;
   117      end;
   118  
   119      class operator &Add(aLeft, aRight: string): string;
   120      begin
   121        var lNew := new byte[aLeft.Length];
   122        for i: Integer := 0 to aLeft.Length - 1 do
   123          lNew[i] := aLeft[i];
   124        result := new string(append(aLeft, aRight.Value));
   125      end;
   126  
   127      class operator &Add(aLeft: string; aRight: Integer): string;
   128      begin
   129        result := aLeft + go.strconv.Itoa(aRight);
   130      end;
   131  
   132      // go does not have Substring method
   133      method Substring(aIndex: int32): string;
   134      begin
   135        result := new string(new Slice<byte>(Value, aIndex, Value.Length - 1));
   136      end;
   137  
   138      method Substring(aIndex: int32; aLen: int32): string;
   139      begin
   140        var lLength := (aIndex + aLen) - 1;
   141        if lLength < 0 then
   142          lLength := 0;
   143        result := new string(new Slice<byte>(Value, aIndex, lLength));
   144      end;
   145  
   146      class method IsNullOrEmpty(aValue: string): Boolean; public;
   147      begin
   148        result := (aValue.Value = nil) or (aValue.Value.Length = 0);
   149      end;
   150  
   151      property Chars[aIndex: int32]: byte read begin
   152        result := Value[aIndex];
   153      end; default; inline;
   154  
   155      property Length: Integer read if Value ≠ nil then Value.Length else 0;
   156  
   157      method GetSequence: sequence of tuple of (Integer, rune); iterator;
   158      begin
   159        var i := 0;
   160        var c := 0;
   161        var r: rune;
   162        while i < Value.Length do begin
   163          r := UTF8ToString(Value, i, var c);
   164          yield (i, r);
   165          i := i + c;
   166        end;
   167      end;
   168  
   169      method Name: string;
   170      begin
   171        result := '';
   172      end;
   173  
   174      method ToString: PlatformString; override;
   175      begin
   176        {$IF ISLAND}
   177        result := Encoding.UTF8.GetString(Value);
   178        {$ELSEIF ECHOES}
   179        result := System.Text.Encoding.UTF8.GetString(Value);
   180        {$ENDIF}
   181      end;
   182  
   183      method GetHashCode: Integer; override; public;
   184      begin
   185        {$IF ISLAND}
   186        if (Value.fArray ≠ nil) and (Value.fArray.Length > 0) then
   187          result := Utilities.CalcHash(^Void(@Value.fArray[0]), Value.Length)
   188        else
   189          result := 0;
   190        {$ELSEIF ECHOES}
   191        result := System.Collections.IStructuralEquatable(Value).GetHashCode(EqualityComparer<byte>.Default);
   192        {$ENDIF}
   193      end;
   194  
   195      method &Equals(obj: Object): Boolean; override; public;
   196      begin
   197        var lOther := string(obj);
   198        if assigned(lOther) then
   199          result := lOther = self;
   200      end;
   201  
   202      class var fZero: string := new string();
   203      class property Zero: string := fZero; published;
   204  
   205      class method UTF8ToString(aValue: array of byte; aIndex: Integer; var aSize: Integer): rune;
   206      begin
   207        if aValue = nil then new ArgumentNullException('aValue is nil');
   208        var len := aValue.Length;
   209        if len = 0 then exit 0;
   210        var pos := aIndex;
   211        var last := aValue.Length;
   212        // skip BOM
   213        if len>2 then begin
   214          if (aValue[0] = $EF) and
   215             (aValue[1] = $BB) and
   216             (aValue[2] = $BF) then pos := 3;
   217        end;
   218          var ch := aValue[pos];
   219          if ch and $F0 = $F0 then begin
   220            //   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
   221            if pos+4 > last then
   222              raise new Exception('Bad UTF8 string');
   223            var code := (((((
   224                          uint32(ch and $7) shl 6 +
   225                          (uint32(aValue[pos+1]) and $3F)) shl 6)+
   226                          (uint32(aValue[pos+2]) and $3F)) shl 6)+
   227                          (uint32(aValue[pos+3]) and $3F));
   228            //if (code < $10000) or (code>$1FFFFF) then MalformedError;
   229            var code1 := code - $10000;
   230            //str.Append(Char($D800 or (code1 shr 10)));
   231            //str.Append(Char($DC00 or (code1 and $3FF)));
   232            aSize := 4;
   233            exit 65; // TODO
   234          end
   235          else if (ch and $E0) = $E0 then begin
   236            //1110xxxx 10xxxxxx 10xxxxxx
   237            if pos+3 > last then
   238              raise new Exception('Bad UTF8 string');
   239            var code := ((
   240                          uint32(ch and $F) shl 6 +
   241                          (uint32(aValue[pos+1]) and $3F)) shl 6)+
   242                          (uint32(aValue[pos+2]) and $3F);
   243            if (code < $800) or (code > $FFFF) then
   244              raise new Exception('Bad UTF8 string');
   245            aSize := 3;
   246            exit code;
   247          end
   248          else if (ch and $C0) = $C0 then begin
   249            // 110xxxxx 10xxxxxx
   250            if pos+2 > last then
   251              raise new Exception('Bad UTF8 string');
   252            var code :=
   253                        uint32(ch and $1F) shl 6 +
   254                        (uint32(aValue[pos+1]) and $3F);
   255            if (code < $80) or (code >$7FF) then
   256              raise new Exception('Bad UTF8 string');
   257            aSize := 2;
   258            exit code;
   259          end
   260          else begin
   261            // 0xxxxxxx
   262            var code := ch;
   263            if (code < $0)  or (code > $7F) then
   264              raise new Exception('Bad UTF8 string');
   265            aSize := 1;
   266            exit code;
   267          end;
   268      end;
   269  
   270      class method PlatformStringArrayToGoArray(aValue: array of PlatformString): array of go.builtin.string;
   271      begin
   272        result := new go.builtin.string[aValue.Length];
   273        for i: Integer := 0 to aValue.Length - 1 do
   274          result[i] := aValue[i];
   275      end;
   276  
   277      class method PlatformStringArrayToGoSlice(aValue: array of PlatformString): Slice<go.builtin.string>;
   278      begin
   279        result := new Slice<go.builtin.string>(aValue.Length);
   280        for i: Integer := 0 to aValue.Length - 1 do
   281          result[i] := aValue[i];
   282      end;
   283    end;
   284  
   285    operator implicit(aVal: string): Slice<Char>; public;
   286    begin
   287      var lString := Encoding.UTF8.GetString(aVal.Value);
   288      result := new Slice<Char>(lString.ToCharArray);
   289    end;
   290  
   291    operator implicit(aVal: string): Slice<go.builtin.rune>; public;
   292    begin
   293      var lString := Encoding.UTF8.GetString(aVal.Value);
   294      result := new Slice<rune>(lString.Select(a -> rune(a)).ToArray());
   295    end;
   296  
   297    operator implicit(aVal: byte): string; public;
   298    begin
   299      exit new string([rune(aVal)]);
   300    end;
   301  
   302    operator implicit(aVal: rune): string; public;
   303    begin
   304      exit new string([aVal]);
   305    end;
   306  
   307    operator Implicit(aVal: Slice<Char>): string; public;
   308    begin
   309      exit new string(aVal.ToArray());
   310    end;
   311  
   312    operator Implicit(aVal: Slice<rune>): string; public;
   313    begin
   314      exit new string(aVal.ToArray());
   315    end;
   316  
   317    operator Explicit(aVal: string): Slice<byte>; public;
   318    begin
   319      result := new Slice<byte>(aVal.Length);
   320      copy(result, aVal.Value);
   321    end;
   322  
   323    operator Explicit(aVal: PlatformString): Slice<byte>; public;
   324    begin
   325      {$IFDEF ISLAND}
   326      exit new Slice<byte>(Encoding.UTF8.GetBytes(aVal));
   327      {$ELSE}
   328      exit new Slice<byte>(System.Text.Encoding.UTF8.GetBytes(aVal));
   329      {$ENDIF}
   330    end;
   331  
   332    operator Explicit(aVal: PlatformString): go.net.http.htmlSig; public;
   333    begin
   334      var q: go.builtin.Slice<byte> := (aVal as go.builtin.Slice<byte>);
   335      {$IF ISLAND}
   336      exit new go.net.http.htmlSig(Value := Encoding.UTF8.GetBytes(aVal));
   337      result := nil;
   338      {$ELSEIF ECHOES}
   339      exit new go.net.http.htmlSig(Value := System.Text.Encoding.UTF8.GetBytes(aVal));
   340      {$ENDIF}
   341    end;
   342  
   343    operator Implicit(aVal: Slice<byte>): string; public;
   344    begin
   345      exit new string(aVal);
   346    end;
   347  
   348    operator Implicit(aVal: PlatformString): Slice<byte>; public;
   349    begin
   350      {$IF ISLAND}
   351      result := new Slice<byte>(Encoding.UTF8.GetBytes(aVal));
   352      {$ELSEIF ECHOES}
   353      result := new Slice<byte>(System.Text.Encoding.UTF8.GetBytes(aVal));
   354      {$ENDIF}
   355    end;
   356  
   357    operator Implicit(aVal: PlatformString): array of byte; public;
   358    begin
   359      {$IF ISLAND}
   360      result := Encoding.UTF8.GetBytes(aVal);
   361      {$ELSEIF ECHOES}
   362      result := System.Text.Encoding.UTF8.GetBytes(aVal);
   363      {$ENDIF}
   364    end;
   365  
   366    operator Implicit(aVal: Slice<byte>): PlatformString; public;
   367    begin
   368      {$IF ISLAND}
   369      result := Encoding.UTF8.GetString(aVal);
   370      {$ELSEIF ECHOES}
   371      result := System.Text.Encoding.UTF8.GetString(aVal);
   372      {$ENDIF}
   373    end;
   374  
   375    operator implicit(aVal: PlatformString): Slice<go.builtin.rune>; public;
   376    begin
   377      if aVal ≠ nil then begin
   378        var lChars := aVal.ToCharArray;
   379        result := new go.builtin.Slice<go.builtin.rune>(lChars.Select(a -> go.builtin.rune(a)).ToArray());
   380      end
   381      else
   382        result := new go.builtin.Slice<go.builtin.rune>(0);
   383    end;
   384  
   385    end.