1882 lines
54 KiB
ObjectPascal
1882 lines
54 KiB
ObjectPascal
/// `buffer` module support bindings for SyNode
|
|
// - this unit is a part of the freeware Synopse framework,
|
|
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
|
|
unit SyNodeBinding_buffer;
|
|
|
|
interface
|
|
{$I Synopse.inc}
|
|
{$I SyNode.inc}
|
|
{$UNDEF HASINLINE}
|
|
type
|
|
TEncoding = (ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER);
|
|
TEndianness = (kLittleEndian, kBigEndian);
|
|
// BINARY is a deprecated alias of LATIN1.
|
|
const LATIN1 = BINARY;
|
|
implementation
|
|
uses
|
|
SysUtils,
|
|
{$IFDEF MSWINDOWS}
|
|
Windows,
|
|
{$ENDIF}
|
|
SynCommons,
|
|
SyNode,
|
|
SpiderMonkey;
|
|
|
|
//const
|
|
// BUFFER_PROTO_SLOT = JSCLASS_GLOBAL_SLOT_COUNT + 1;
|
|
|
|
|
|
const
|
|
_Endianness: record
|
|
case boolean of
|
|
true: (u8: array[0..1] of UInt8);
|
|
false: (u16: uint16);
|
|
end = (u8: (1, 0));
|
|
|
|
var
|
|
Endianness: TEndianness;
|
|
|
|
const kMaxLength = (1 shl 28) - 16;
|
|
|
|
const OUT_OF_RANGE = 'out of range index';
|
|
NOT_A_HEX = 'Not a hex';
|
|
|
|
procedure getBufDataAndLength(cx: PJSContext; bufObj: PJSRootedObject; out bufData: pointer; out bufLen: size_t);{$ifdef HASINLINE}inline;{$endif}
|
|
const MUST_BE_A_BUFFER = 'argument should be a Buffer';
|
|
begin
|
|
if not bufObj.ptr.IsTypedArrayObject then
|
|
raise ESMTypeException.Create(MUST_BE_A_BUFFER);
|
|
bufData := Pointer(bufObj.ptr.GetUint8ArrayData);{ + bufObj.ptr.GetTypedArrayByteOffset}
|
|
bufLen := bufObj.ptr.GetTypedArrayByteLength;
|
|
end;
|
|
|
|
function ParseEncoding(const encoding: RawUTF8; default_encoding: TEncoding): TEncoding; {$ifdef HASINLINE}inline;{$endif}
|
|
begin
|
|
Result := default_encoding;
|
|
case Length(encoding) of
|
|
3: if IdemPChar(Pointer(encoding), 'HEX') then
|
|
Result := HEX;
|
|
4: if IdemPChar(Pointer(encoding), 'UTF8') then
|
|
Result := UTF8
|
|
else if IdemPChar(Pointer(encoding), 'UCS2') then
|
|
Result := UCS2;
|
|
5: if IdemPChar(Pointer(encoding), 'UTF-8') then
|
|
Result := UTF8
|
|
else if IdemPChar(Pointer(encoding), 'UCS-2') then
|
|
Result := UCS2
|
|
else if IdemPChar(Pointer(encoding), 'ASCII') then
|
|
Result := ASCII;
|
|
6: if IdemPChar(Pointer(encoding), 'BASE64') then
|
|
Result := BASE64
|
|
else if IdemPChar(Pointer(encoding), 'LATIN1') then
|
|
Result := LATIN1
|
|
else if IdemPChar(Pointer(encoding), 'BINARY') then
|
|
Result := LATIN1 // BINARY is a deprecated alias of LATIN1.
|
|
else if IdemPChar(Pointer(encoding), 'BUFFER') then
|
|
Result := BUFFER;
|
|
7: if IdemPChar(Pointer(encoding), 'UTF16LE') then
|
|
Result := UCS2;
|
|
8: if IdemPChar(Pointer(encoding), 'UTF16-LE') then
|
|
Result := UCS2;
|
|
end;
|
|
end;
|
|
|
|
function StringSlice(cx: PJSContext; argc: uintN; var vp: jsargRec; encoding: TEncoding): Boolean; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
bufData: pointer;
|
|
length: size_t;
|
|
tmp_str: RawByteString;
|
|
p: PAnsiChar;
|
|
in_argv: PjsvalVector;
|
|
start: size_t;
|
|
end_: size_t;
|
|
bufLen: size_t;
|
|
this: PJSRootedObject;
|
|
const
|
|
TWO_ARGRUMENTS_EXPECTED = '2 arguments expected';
|
|
begin
|
|
try
|
|
Result := True;
|
|
this := cx.NewRootedObject(vp.thisObject[cx]);
|
|
try
|
|
getBufDataAndLength(cx, this, bufData, bufLen);
|
|
finally
|
|
cx.FreeRootedObject(this);
|
|
end;
|
|
if bufLen = 0 then begin
|
|
{$IFDEF SM52}
|
|
vp.rval := cx.EmptyString.ToJSVal;
|
|
{$ELSE}
|
|
vp.rval := cx.rt.EmptyString.ToJSVal;
|
|
{$ENDIF}
|
|
Exit;
|
|
end;
|
|
|
|
if argc < 2 then
|
|
raise ESMException.Create(TWO_ARGRUMENTS_EXPECTED);
|
|
|
|
in_argv := vp.argv;
|
|
if in_argv[0].isNumber then
|
|
start := in_argv[0].asInt64
|
|
else
|
|
start := 0;
|
|
|
|
bufData := PAnsiChar(bufData) + start; //Pointer(UIntPtr(bufData) + start);
|
|
|
|
if in_argv[1].isNumber then
|
|
end_ := in_argv[1].asInt64
|
|
else
|
|
end_ := bufLen;
|
|
if end_ < start then
|
|
end_ := start;
|
|
if end_ > bufLen then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
length := end_ - start;
|
|
case encoding of
|
|
// todo: check ascii(and $7F)
|
|
ASCII: begin
|
|
SetString(tmp_str, PAnsiChar(bufData), length);
|
|
p := pointer(tmp_str);
|
|
while length >= 4 do begin
|
|
PCardinal(p)^ := PCardinal(p)^ and $7F7F7F7F;
|
|
Inc(p,4);
|
|
dec(length, 4);
|
|
end;
|
|
if length >= 2 then begin
|
|
PWord(p)^ := PWord(p)^ and $7F7F;
|
|
Inc(p,2);
|
|
dec(length, 2);
|
|
end;
|
|
if length >= 1 then begin
|
|
PByte(p)^ := PByte(p)^ and $7F;
|
|
Inc(p,1);
|
|
end;
|
|
|
|
vp.rval := JS_NewStringCopyN(cx, Pointer(tmp_str), System.length(tmp_str)).ToJSVal;
|
|
end;
|
|
|
|
LATIN1: vp.rval := JS_NewStringCopyN(cx, bufData, length).ToJSVal;
|
|
UTF8: vp.rval := cx.NewJSString(bufData, length, CP_UTF8).ToJSVal;
|
|
BASE64: vp.rval := cx.NewJSString(BinToBase64(bufData, length)).ToJSVal;
|
|
//todo: optimize
|
|
HEX: vp.rval := cx.NewJSString(LowerCase(BinToHex(bufData, length))).ToJSVal;
|
|
UCS2: vp.rval := cx.NewJSString(bufData, length div 2).ToJSVal;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function cpSlice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
bufData: pointer;
|
|
length: size_t;
|
|
in_argv: PjsvalVector;
|
|
start: size_t;
|
|
end_: size_t;
|
|
bufLen: size_t;
|
|
this: PJSRootedObject;
|
|
destCP: integer;
|
|
const
|
|
USAGE = 'cpSlice(start, length, cp: integer)';
|
|
begin
|
|
try
|
|
Result := True;
|
|
this := cx.NewRootedObject(vp.thisObject[cx]);
|
|
try
|
|
getBufDataAndLength(cx, this, bufData, bufLen);
|
|
finally
|
|
cx.FreeRootedObject(this);
|
|
end;
|
|
if bufLen = 0 then begin
|
|
{$IFDEF SM52}
|
|
vp.rval := cx.EmptyString.ToJSVal;
|
|
{$ELSE}
|
|
vp.rval := cx.rt.EmptyString.ToJSVal;
|
|
{$ENDIF}
|
|
Exit;
|
|
end;
|
|
|
|
if argc < 3 then
|
|
raise ESMException.Create(USAGE);
|
|
|
|
in_argv := vp.argv;
|
|
if in_argv[0].isNumber then
|
|
start := in_argv[0].asInt64
|
|
else
|
|
start := 0;
|
|
|
|
bufData := PAnsiChar(bufData) + start;
|
|
|
|
if in_argv[1].isNumber then
|
|
end_ := in_argv[1].asInt64
|
|
else
|
|
end_ := bufLen;
|
|
|
|
if end_ < start then
|
|
end_ := start;
|
|
if end_ > bufLen then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
length := end_ - start;
|
|
|
|
if in_argv[2].isNumber then
|
|
destCP := in_argv[2].asInteger
|
|
else
|
|
destCP := CP_UTF8;
|
|
vp.rval := cx.NewJSString(bufData, length, destCP).ToJSVal;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function asciiSlice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringSlice(cx, argc, vp, ASCII);
|
|
end;
|
|
|
|
function latin1Slice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringSlice(cx, argc, vp, LATIN1);
|
|
end;
|
|
|
|
function utf8Slice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringSlice(cx, argc, vp, UTF8);
|
|
end;
|
|
|
|
function base64Slice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringSlice(cx, argc, vp, BASE64);
|
|
end;
|
|
|
|
function hexSlice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringSlice(cx, argc, vp, HEX);
|
|
end;
|
|
|
|
function ucs2Slice(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringSlice(cx, argc, vp, UCS2);
|
|
end;
|
|
|
|
function isFirstByteInChar(value: byte): boolean; {$ifdef HASINLINE}inline;{$endif}
|
|
//0xxxxxxx or 11xxxxxx
|
|
begin
|
|
result := (value < 128) or (value >= 192);
|
|
end;
|
|
|
|
/// This array is a lookup table that translates AnsiChar characters drawn from the "Base64 Alphabet" (as specified
|
|
// in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64
|
|
// alphabet but fall within the bounds of the array are translated to -1.
|
|
//
|
|
// Whitespaces are translated to -2.
|
|
//
|
|
// Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both
|
|
// URL_SAFE and STANDARD base64.
|
|
//
|
|
// Thanks to "commons" project in ws.apache.org for this code.
|
|
// http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
|
|
const
|
|
DECODE_TABLE: array[AnsiChar] of shortint = (
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, // 00-0f
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
|
|
-2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - /
|
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9
|
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _
|
|
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o
|
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 70-7a p-z
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
|
);
|
|
|
|
/// Conversion from URL_SAFE and STANDARD base64 encoded text into binary data with features:
|
|
// - padding is optional
|
|
// - whitespaces (' ', \r, \n) are skipped to be compartible with
|
|
// transfer encoding for MIME (RFC 2045);
|
|
// for example SOAP/E-Mail use \r\n to split long Base64 string into parts
|
|
// - will decode up to maxBinLength into bin buffer
|
|
// - can optinaly accept a 2 bytes representation of str (0xC1 0x0 0xC2 0x0 ...) as
|
|
// returned by SpiderMonkey engine GetTwoByteStringCharsAndLength function.
|
|
// In this case strLen is duplet count
|
|
// Return length of resulting bin or 0 in case of invalid Base64 input
|
|
//
|
|
// To estimate a length of result buffer function should be called as
|
|
// estimatedLength := anyBase64ToBin(str, strLen, nil, strLen);
|
|
function anyBase64ToBin(str: PAnsiChar; strLen: PtrInt;
|
|
bin: PAnsiChar; maxBinLength: PtrInt;
|
|
strIsTwoBytes: boolean = false): PtrInt;
|
|
var
|
|
maxS, maxB, bPos, sPos: PtrInt;
|
|
v: uint32;
|
|
c: AnsiChar;
|
|
mul: byte;
|
|
|
|
function failTolerantDecode: boolean;
|
|
var lo, hi: smallint;
|
|
begin
|
|
repeat
|
|
c := str[sPos]; lo := DECODE_TABLE[c];
|
|
inc(sPos, 1 shl mul);
|
|
if (lo >= 0) then break; // Legal character
|
|
if ((c = '=') or (sPos >= strLen)) then
|
|
exit(false); // Stop decoding
|
|
until false;
|
|
//exp
|
|
if (sPos >= strLen) then exit(false);
|
|
if (bPos >= maxBinLength) then exit(false);
|
|
hi := lo;
|
|
|
|
// first byte
|
|
repeat
|
|
c := str[sPos]; lo := DECODE_TABLE[c];
|
|
inc(sPos, 1 shl mul);
|
|
if (lo >= 0) then break; // Legal character
|
|
if ((c = '=') or (sPos >= strLen)) then
|
|
exit(false); // Stop decoding
|
|
until false;
|
|
if bin <> nil then
|
|
bin[bPos] := AnsiChar( ((hi and $3F) shl 2) or ((lo and $30) shr 4) );
|
|
inc(bPos);
|
|
if (sPos >= strLen) then exit(false);
|
|
if (bPos >= maxBinLength) then exit(false);
|
|
hi := lo;
|
|
|
|
// second byte
|
|
repeat
|
|
c := str[sPos]; lo := DECODE_TABLE[c];
|
|
inc(sPos, 1 shl mul);
|
|
if (lo >= 0) then break; // Legal character
|
|
if ((c = '=') or (sPos >= strLen)) then
|
|
exit(false); // Stop decoding
|
|
until false;
|
|
if bin <> nil then
|
|
bin[bPos] := AnsiChar( ((hi and $0F) shl 4) or ((lo and $3C) shr 2) );
|
|
inc(bPos);
|
|
if (sPos >= strLen) then exit(false);
|
|
if (bPos >= maxBinLength) then exit(false);
|
|
hi := lo;
|
|
|
|
// third byte
|
|
repeat
|
|
c := str[sPos]; lo := DECODE_TABLE[c];
|
|
inc(sPos, 1 shl mul);
|
|
if (lo >= 0) then break; // Legal character
|
|
if ((c = '=') or (sPos >= strLen)) then
|
|
exit(false); // Stop decoding
|
|
until false;
|
|
if bin <> nil then
|
|
bin[bPos] := AnsiChar( ((hi and $03) shl 6) or ((lo and $3F) shr 0) );
|
|
inc(bPos);
|
|
if (sPos >= strLen) then exit(false);
|
|
if (bPos >= maxBinLength) then exit(false);
|
|
Result := true;
|
|
end;
|
|
begin
|
|
Result := 0;
|
|
if strIsTwoBytes then begin
|
|
mul := 1; strLen := strLen shl mul;
|
|
end else
|
|
mul := 0;
|
|
maxB := (maxBinLength div 3) * 3;
|
|
maxS := (strLen div (4 shl mul)) * (4 shl mul);
|
|
bPos := 0;
|
|
sPos := 0;
|
|
while (bPos < maxB) and (sPos < maxS) do begin
|
|
v := DECODE_TABLE[str[sPos + (0 shl mul)]] shl 24 or
|
|
DECODE_TABLE[str[sPos + (1 shl mul)]] shl 16 or
|
|
DECODE_TABLE[str[sPos + (2 shl mul)]] shl 8 or
|
|
DECODE_TABLE[str[sPos + (3 shl mul)]];
|
|
// If MSB is set, input contains whitespace or is not valid base64
|
|
if v and $80808080 > 0 then begin
|
|
if not failTolerantDecode() then
|
|
exit(bPos);
|
|
maxB := bPos + (strLen shr mul - bPos) div 4 * 4; // Align maxB again
|
|
end else begin
|
|
if bin <> nil then begin
|
|
bin[bPos + 0] := AnsiChar( ((v shr 22) and $FC) or ((v shr 20) and $03) );
|
|
bin[bPos + 1] := AnsiChar( ((v shr 12) and $F0) or ((v shr 10) and $0F) );
|
|
bin[bPos + 2] := AnsiChar( ((v shr 2) and $C0) or ((v shr 0) and $3F) );
|
|
end;
|
|
inc(bPos, 3);
|
|
inc(sPos, 4 shl mul);
|
|
end;
|
|
end;
|
|
if (sPos < strLen) and (bPos < maxBinLength) then
|
|
failTolerantDecode();
|
|
Result := bPos;
|
|
end;
|
|
|
|
function StringBytesWrite(bufData: Pointer; max_length: size_t; str: Pointer; strLen: size_t; isLatin1: Boolean; encoding:TEncoding): size_t; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
strLenInBytes: size_t;
|
|
i: PtrInt;
|
|
ch: PAnsiChar;
|
|
begin
|
|
if isLatin1 then
|
|
strLenInBytes := strLen
|
|
else
|
|
strLenInBytes := strLen * 2;
|
|
|
|
Result := strLenInBytes;
|
|
if (encoding = HEX) then Result := Result shr 1;
|
|
if isLatin1 and (encoding = UCS2) then Result := Result shl 1;
|
|
if not isLatin1 and (encoding in [ASCII, LATIN1]) then Result := Result shr 1;
|
|
if Result > max_length then
|
|
Result := max_length;
|
|
|
|
case encoding of
|
|
ASCII, LATIN1: begin
|
|
if isLatin1 then begin
|
|
MoveFast(str^, bufData^, Result)
|
|
end else begin
|
|
for i := 0 to Result - 1 do
|
|
Puint8Vector(bufData)[i] := Puint8Vector(str)[i*2];
|
|
end;
|
|
end;
|
|
UTF8: begin
|
|
if isLatin1 then begin
|
|
if Result < strLen then
|
|
while (Result > 0) and not isFirstByteInChar(pByte(PtrUInt(str)+Result)^) do
|
|
Dec(Result);
|
|
MoveFast(str^, bufData^, Result);
|
|
end else begin
|
|
raise ESMException.Create('Critical Error');
|
|
end;
|
|
end;
|
|
UCS2: begin
|
|
if isLatin1 then begin
|
|
// todo: optimize
|
|
// SetLength(UnicodeBuf, Result shr 1);
|
|
// WinAnsiConvert.AnsiBufferToUnicode(Pointer(UnicodeBuf), str, Result shr 1);
|
|
// MoveFast(Pointer(UnicodeBuf)^, bufData^, Result);
|
|
Latin1AnsiConvert.AnsiBufferToUnicode(bufData, str, Result shr 1, true);
|
|
end else begin
|
|
Result := Result and (not 1);
|
|
MoveFast(str^, bufData^, Result);
|
|
end;
|
|
end;
|
|
BASE64: begin
|
|
i := anyBase64ToBin(str, strLen, bufData, max_length, not isLatin1);
|
|
if i = -1 then
|
|
Result := 0 // error in base64
|
|
else if i > max_length then
|
|
Result := max_length
|
|
else
|
|
Result := i;
|
|
end;
|
|
HEX: begin
|
|
if isLatin1 then begin
|
|
ch := PAnsiChar(str);
|
|
for i := 0 to Result - 1 do begin
|
|
if (ConvertHexToBin[Ord(ch^)] > 15) or
|
|
(ConvertHexToBin[Ord((ch+1)^)] > 15)
|
|
then begin
|
|
Result := i;
|
|
Break;
|
|
end;
|
|
inc(ch, 2);
|
|
end;
|
|
HexToBin(str, bufData, Result);
|
|
end else begin
|
|
raise ESMTypeException.Create(NOT_A_HEX);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function StringWrite(cx: PJSContext; argc: uintN; var vp: jsargRec; encoding: TEncoding): Boolean; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
bufData: pointer;
|
|
str: Pointer;
|
|
strUtf8: RawUTF8;
|
|
strLen: size_t;
|
|
bytesWrite: size_t;
|
|
isLatin1: Boolean;
|
|
|
|
in_argv: PjsvalVector;
|
|
_str: PJSString;
|
|
offset: size_t;
|
|
max_length: size_t;
|
|
bufLen: size_t;
|
|
this: PJSRootedObject;
|
|
|
|
const
|
|
THREE_ARGRUMENTS_EXPECTED = '3 arguments expected';
|
|
FIRST_ARGRUMENT_MUST_BE_A_STRING = 'Argument must be a string';
|
|
OFFSET_IS_OUT_OF_BOUNDS = 'Offset is out of bounds';
|
|
begin
|
|
try
|
|
Result := True;
|
|
|
|
if argc <3 then
|
|
raise ESMException.Create(THREE_ARGRUMENTS_EXPECTED);
|
|
in_argv := vp.argv;
|
|
if not in_argv[0].isString then
|
|
raise ESMException.Create(FIRST_ARGRUMENT_MUST_BE_A_STRING);
|
|
|
|
_str := in_argv[0].asJSString;
|
|
if encoding = UTF8 then begin
|
|
strUtf8 := _str.ToUTF8(cx);
|
|
str := Pointer(strUtf8);
|
|
strLen := Length(strUtf8);
|
|
isLatin1 := True;
|
|
encoding := UTF8;
|
|
end else begin
|
|
isLatin1 := _str.HasLatin1Chars;
|
|
if isLatin1 then
|
|
str := _str.GetLatin1StringCharsAndLength(cx, strLen)
|
|
else begin
|
|
str := _str.GetTwoByteStringCharsAndLength(cx, strLen);
|
|
end;
|
|
end;
|
|
|
|
if (encoding = HEX) and (strLen and 1 = 1) then
|
|
raise ESMTypeException.Create(NOT_A_HEX);
|
|
|
|
this := cx.NewRootedObject(vp.thisObject[cx]);
|
|
try
|
|
getBufDataAndLength(cx, this,bufData, bufLen);
|
|
finally
|
|
cx.FreeRootedObject(this);
|
|
end;
|
|
|
|
if in_argv[1].isNumber then
|
|
offset := in_argv[1].asInt64
|
|
else
|
|
offset := 0;
|
|
|
|
bufData := Pointer(UIntPtr(bufData) + offset);
|
|
|
|
if offset > bufLen then
|
|
raise ESMRangeException.Create(OFFSET_IS_OUT_OF_BOUNDS);
|
|
|
|
if in_argv[2].isNumber then
|
|
max_length := in_argv[2].asInt64
|
|
else
|
|
max_length := bufLen - offset;
|
|
|
|
if max_length > bufLen - offset then
|
|
max_length := bufLen - offset;
|
|
|
|
if max_length = 0 then begin
|
|
vp.rval := SimpleVariantToJSval(cx, 0);
|
|
exit;
|
|
end;
|
|
|
|
bytesWrite := StringBytesWrite(bufData, max_length, str, strLen, isLatin1, encoding);
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, bytesWrite);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function asciiWrite(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringWrite(cx, argc, vp, ASCII);
|
|
end;
|
|
|
|
function Latin1Write(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringWrite(cx, argc, vp, LATIN1);
|
|
end;
|
|
|
|
function utf8Write(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringWrite(cx, argc, vp, UTF8);
|
|
end;
|
|
|
|
function ucs2Write(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringWrite(cx, argc, vp, UCS2);
|
|
end;
|
|
|
|
function base64Write(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringWrite(cx, argc, vp, BASE64);
|
|
end;
|
|
|
|
function hexWrite(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := StringWrite(cx, argc, vp, HEX);
|
|
end;
|
|
|
|
// bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
|
|
function Copy(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
this, target: PJSRootedObject;
|
|
sourceData, targetData: Pointer;
|
|
val_i64: Int64;
|
|
sourceLength, targetLength: size_t;
|
|
targetStart, sourceStart, sourceEnd: size_t;
|
|
to_copy: UInt32;
|
|
in_argv: PjsvalVector;
|
|
const
|
|
sInvalidCall = 'usage: copy(target: Buffer, [targetStart = 0], [sourceStart = 0], [sourceEnd = this.length]);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 1) or (not in_argv[0].isObject) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
this := cx.NewRootedObject(vp.thisObject[cx]);
|
|
try
|
|
getBufDataAndLength(cx, this, sourceData, sourceLength);
|
|
finally
|
|
cx.FreeRootedObject(this);
|
|
end;
|
|
|
|
target := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, target, targetData, targetLength);
|
|
finally
|
|
cx.FreeRootedObject(target);
|
|
end;
|
|
|
|
if (argc > 1) and (in_argv[1].isNumber) then begin
|
|
val_i64 := in_argv[1].asInt64;
|
|
if (val_i64 < $FFFFFFFF) and (val_i64 >= 0) then
|
|
targetStart := val_i64
|
|
else
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
end else
|
|
targetStart := 0;
|
|
|
|
if (argc > 2) and (in_argv[2].isNumber) then begin
|
|
val_i64 := in_argv[2].asInt64;
|
|
if (val_i64 < $FFFFFFFF) and (val_i64 >= 0) then
|
|
sourceStart := val_i64
|
|
else
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
end else
|
|
sourceStart := 0;
|
|
|
|
if (argc > 3) and (in_argv[3].isNumber) then begin
|
|
val_i64 := in_argv[3].asInt64;
|
|
if (val_i64 < $FFFFFFFF) and (val_i64 >= 0) then
|
|
sourceEnd := val_i64
|
|
else
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
end else
|
|
sourceEnd := sourceLength;
|
|
|
|
if (targetStart >= targetLength) or (sourceStart >= sourceEnd) then begin
|
|
vp.rval := SimpleVariantToJSval(cx, 0);
|
|
exit;
|
|
end;
|
|
|
|
if (sourceStart > sourceLength) then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
|
|
if (sourceEnd - sourceStart > targetLength - targetStart) then
|
|
sourceEnd := sourceStart + targetLength - targetStart;
|
|
|
|
if sourceEnd - sourceStart > targetLength - targetStart then
|
|
to_copy := targetLength - targetStart
|
|
else
|
|
to_copy := sourceEnd - sourceStart;
|
|
|
|
if to_copy > targetLength - sourceStart then
|
|
to_copy := targetLength - sourceStart;
|
|
|
|
MoveFast(Pointer(UIntPtr(sourceData) + sourceStart)^, Pointer(UIntPtr(targetData) + targetStart)^, to_copy);
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, to_copy);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function setupBufferJS(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
proto: PJSRootedObject;
|
|
const
|
|
sInvalidCall = 'usage: setupBufferJS(proto: Object);';
|
|
props = JSPROP_ENUMERATE or JSPROP_ENUMERATE or JSPROP_PERMANENT;
|
|
// From GlobalObject.h getOrCreateTypedArrayPrototype
|
|
TypedArraySlotIndex = JSCLASS_GLOBAL_APPLICATION_SLOTS + Ord(JSProto_LIMIT) + Ord(JSProto_TypedArray);
|
|
begin
|
|
try
|
|
Result := True;
|
|
|
|
in_argv := vp.argv;
|
|
if (argc < 1) or not (in_argv[0].isObject) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
proto := cx.NewRootedObject(in_argv[0].asObject);
|
|
|
|
try
|
|
//set_buffer_prototype_object
|
|
vp.thisObject[cx].ReservedSlot[0] := proto.ptr.ToJSValue;
|
|
proto.ptr.DefineFunction(cx, 'asciiSlice', asciiSlice, 2, props);
|
|
proto.ptr.DefineFunction(cx, 'base64Slice', base64Slice, 2, props);
|
|
proto.ptr.DefineFunction(cx, 'latin1Slice', latin1Slice, 2, props);
|
|
proto.ptr.DefineFunction(cx, 'hexSlice', hexSlice, 2, props);
|
|
proto.ptr.DefineFunction(cx, 'ucs2Slice', ucs2Slice, 2, props);
|
|
proto.ptr.DefineFunction(cx, 'utf8Slice', utf8Slice, 2, props);
|
|
proto.ptr.DefineFunction(cx, 'cpSlice', cpSlice, 2, props);
|
|
|
|
proto.ptr.DefineFunction(cx, 'asciiWrite', asciiWrite, 3, props);
|
|
proto.ptr.DefineFunction(cx, 'base64Write', base64Write, 3, props);
|
|
proto.ptr.DefineFunction(cx, 'latin1Write', asciiWrite, 3, props); // the same as asciiWrite
|
|
proto.ptr.DefineFunction(cx, 'hexWrite', hexWrite, 3, props);
|
|
proto.ptr.DefineFunction(cx, 'ucs2Write', ucs2Write, 3, props);
|
|
proto.ptr.DefineFunction(cx, 'utf8Write', utf8Write, 3, props);
|
|
|
|
proto.ptr.DefineFunction(cx, 'copy', Copy, 0, props);
|
|
finally
|
|
cx.FreeRootedObject(proto);
|
|
end;
|
|
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function createFromString(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
encoding: TEncoding;
|
|
tmp: RawByteString;
|
|
|
|
_str: PJSString;
|
|
isLatin1: Boolean;
|
|
str: Pointer;
|
|
strLen: size_t;
|
|
strUtf8: RawUTF8;
|
|
|
|
buf: PJSRootedObject;
|
|
bufData: Pointer;
|
|
size: PtrInt;
|
|
actual: size_t;
|
|
proto: PJSRootedObject;
|
|
val: jsval;
|
|
const
|
|
sInvalidCall = 'usage: createFromString(string, encoding: String);';
|
|
UNKNOWN_ENCODING = 'unknown encoding';
|
|
begin
|
|
try
|
|
Result := True;
|
|
|
|
in_argv := vp.argv;
|
|
if (argc < 2) or (not in_argv[0].isString) or (not in_argv[1].isString) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
encoding := ParseEncoding(in_argv[1].asJSString.ToUTF8(cx), UTF8);
|
|
_str := in_argv[0].asJSString;
|
|
|
|
if encoding = UTF8 then begin
|
|
strUtf8 := _str.ToUTF8(cx);
|
|
str := Pointer(strUtf8);
|
|
strLen := Length(strUtf8);
|
|
isLatin1 := True;
|
|
encoding := UTF8;
|
|
end else begin
|
|
isLatin1 := _str.HasLatin1Chars;
|
|
if isLatin1 then
|
|
str := _str.GetLatin1StringCharsAndLength(cx, strLen)
|
|
else begin
|
|
str := _str.GetTwoByteStringCharsAndLength(cx, strLen);
|
|
end;
|
|
end;
|
|
|
|
case encoding of
|
|
ASCII, LATIN1: size := strLen;
|
|
BUFFER, UTF8: size := Length(_str.ToUTF8(cx));//todo fix it
|
|
UCS2: size := strLen shl 1;
|
|
BASE64: begin
|
|
size := (strLen * 3) shr 2 + 2; // estimate
|
|
setLength(tmp, size);
|
|
size := anyBase64ToBin(str, strLen, pointer(tmp), size, not isLatin1);
|
|
if size < 0 then size := 0;
|
|
proto := cx.NewRootedObject(vp.thisObject[cx].ReservedSlot[0].asObject.Ctor[cx]);
|
|
try
|
|
val.asInteger := size;
|
|
buf := cx.NewRootedObject(cx.New(proto.ptr, 1, @val));
|
|
try
|
|
bufData := buf.ptr.GetUint8ArrayData;
|
|
vp.rval := buf.ptr.ToJSValue;
|
|
finally
|
|
cx.FreeRootedObject(buf);
|
|
end;
|
|
MoveFast(pointer(tmp)^, bufData^, size);
|
|
finally
|
|
cx.FreeRootedObject(proto);
|
|
end;
|
|
exit;
|
|
end;
|
|
HEX: size := strLen shr 1;
|
|
else
|
|
raise ESMException.Create(UNKNOWN_ENCODING);
|
|
end;
|
|
|
|
proto := cx.NewRootedObject(vp.thisObject[cx].ReservedSlot[0].asObject.Ctor[cx]);
|
|
try
|
|
val.asInteger := size;
|
|
buf := cx.NewRootedObject(cx.New(proto.ptr, 1, @val));
|
|
try
|
|
bufData := buf.ptr.GetUint8ArrayData;
|
|
vp.rval := buf.ptr.ToJSValue;
|
|
finally
|
|
cx.FreeRootedObject(buf);
|
|
end;
|
|
|
|
actual := StringBytesWrite(bufData, size, str, strLen, isLatin1, encoding);
|
|
if actual = 0 then begin
|
|
val.asInteger := 0;
|
|
buf := cx.NewRootedObject(cx.New(proto.ptr, 1, @val));
|
|
try
|
|
vp.rval := buf.ptr.ToJSValue;
|
|
finally
|
|
cx.FreeRootedObject(buf);
|
|
end;
|
|
end
|
|
finally
|
|
cx.FreeRootedObject(proto);
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function byteLengthUtf8(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
const
|
|
sInvalidCall = 'usage: byteLengthUtf8(string: String);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 1) or (not in_argv[0].isString) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
vp.rval := SimpleVariantToJSval(cx, Length(in_argv[0].asJSString.ToUTF8(cx)));
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function memcmp(const X, Y; Size: size_t): Integer;
|
|
begin
|
|
result := StrLIComp(PAnsiChar(@x), PAnsiChar(@y) ,size);
|
|
end;
|
|
//asm
|
|
// mov esi,X
|
|
// mov edi,Y
|
|
// mov ecx,Size
|
|
// mov dl,cl
|
|
// and dl,3
|
|
// shr ecx,2
|
|
// xor eax,eax
|
|
// rep cmpsd
|
|
// jb @@less
|
|
// ja @@great
|
|
// mov cl,dl
|
|
// rep cmpsb
|
|
// jz @@end
|
|
// ja @@great
|
|
// @@less:
|
|
// dec eax
|
|
// jmp @@end
|
|
// @@great:
|
|
// inc eax
|
|
// @@end:
|
|
//end;
|
|
|
|
function compare(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
bufa, bufb: PJSRootedObject;
|
|
bufa_data, bufb_data: Pointer;
|
|
bufa_length, bufb_length: size_t;
|
|
cmp_length: size_t;
|
|
res: Integer;
|
|
const
|
|
sInvalidCall = 'usage: compare(buf1, buf: Buffer);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 2) or not (in_argv[0].isObject) or not (in_argv[1].isObject) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
bufa := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, bufa, bufa_data, bufa_length);
|
|
finally
|
|
cx.FreeRootedObject(bufa);
|
|
end;
|
|
|
|
bufb := cx.NewRootedObject(in_argv[1].asObject);
|
|
try
|
|
getBufDataAndLength(cx, bufb, bufb_data, bufb_length);
|
|
finally
|
|
cx.FreeRootedObject(bufb);
|
|
end;
|
|
|
|
if bufa_length > bufb_length then
|
|
cmp_length := bufb_length
|
|
else
|
|
cmp_length := bufa_length;
|
|
|
|
if cmp_length > 0 then
|
|
res := memcmp(bufa_data^, bufb_data^, cmp_length)
|
|
else
|
|
res := 0;
|
|
|
|
if res = 0 then begin
|
|
if bufa_length > bufb_length then
|
|
res := 1
|
|
else if bufb_length > bufa_length then
|
|
res := -1;
|
|
end else begin
|
|
if res > 0 then
|
|
res := 1
|
|
else
|
|
res := -1;
|
|
end;
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, res);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function compareOffset(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
target, source: PJSRootedObject;
|
|
targetData, sourceData: Pointer;
|
|
target_start, source_start, source_end, target_end, source_length, target_length: size_t;
|
|
to_cmp: size_t;
|
|
res: Integer;
|
|
const
|
|
sInvalidCall = 'usage: compareOffset(source, target: Buffer; start, sourceStart[, targetEnd= target.length][, sourceEnd=source.length]: Number);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 4) or not (in_argv[0].isObject) or not (in_argv[1].isObject)
|
|
or not(in_argv[2].isNumber) or not (in_argv[3].isNumber)
|
|
then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
source := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, source, sourceData, source_length);
|
|
finally
|
|
cx.FreeRootedObject(source);
|
|
end;
|
|
|
|
target := cx.NewRootedObject(in_argv[1].asObject);
|
|
try
|
|
getBufDataAndLength(cx, target, targetData, target_length);
|
|
finally
|
|
cx.FreeRootedObject(target);
|
|
end;
|
|
|
|
target_start := in_argv[2].asInt64;
|
|
source_start := in_argv[3].asInt64;
|
|
if (argc > 4) and in_argv[4].isNumber then
|
|
target_end := in_argv[4].asInt64
|
|
else
|
|
target_end := source_length;
|
|
if (argc > 5) and in_argv[4].isNumber then
|
|
source_end := in_argv[5].asInt64
|
|
else
|
|
source_end := target_length;
|
|
|
|
if source_start > source_length then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
if target_start > target_length then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
|
|
if source_start > source_end then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
if target_start > target_end then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
|
|
if source_end - source_start > target_end - target_start then
|
|
to_cmp := target_end - target_start
|
|
else
|
|
to_cmp := source_end - source_start;
|
|
|
|
if to_cmp > source_length - source_start then
|
|
to_cmp := source_length - source_start;
|
|
|
|
if to_cmp > 0 then
|
|
res := memcmp(Pointer(UIntPtr(sourceData) + source_start)^, Pointer(UIntPtr(targetData) + target_start)^, to_cmp)
|
|
else
|
|
res := 0;
|
|
|
|
if res = 0 then begin
|
|
if source_end - source_start > target_end - target_start then
|
|
res := 1
|
|
else if target_end - target_start > source_end - source_start then
|
|
res := -1;
|
|
end else begin
|
|
if res > 0 then
|
|
res := 1
|
|
else
|
|
res := -1;
|
|
end;
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, res);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function fill(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
target: PJSRootedObject;
|
|
targetData: Pointer;
|
|
targetLength: size_t;
|
|
_start, _end, fill_length: size_t;
|
|
str: PJSString;
|
|
str_length: size_t;
|
|
str_lengthInBytes: size_t;
|
|
str_data: Pointer;
|
|
isLatin1: Boolean;
|
|
encoding: TEncoding;
|
|
strUtf8: RawUTF8;
|
|
|
|
valObj: PJSRootedObject;
|
|
valData: Pointer;
|
|
valLength: size_t;
|
|
Need_start_fill: boolean;
|
|
|
|
valByte: Byte;
|
|
|
|
in_there: size_t;
|
|
ptr: Pointer;
|
|
const
|
|
sInvalidCall = 'usage: fill(target: Buffer; val: *; start, end: Number[; encoding: String = "UTF8"]);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 4) or (not in_argv[0].isObject) or (not in_argv[2].isNumber) or (not in_argv[3].isNumber) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
vp.rval := JSVAL_VOID;
|
|
target := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, target, targetData, targetLength);
|
|
finally
|
|
cx.FreeRootedObject(target);
|
|
end;
|
|
|
|
_start := in_argv[2].asInt64;
|
|
_end := in_argv[3].asInt64;
|
|
fill_length := _end - _start;
|
|
|
|
if fill_length + _start > targetLength then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
Need_start_fill := False;
|
|
// First check if Buffer has been passed.
|
|
if in_argv[1].isObject and not in_argv[1].isNull then begin
|
|
valObj := cx.NewRootedObject(in_argv[1].asObject);
|
|
try
|
|
if valObj.ptr.IsTypedArrayObject then begin
|
|
getBufDataAndLength(cx, valObj, valData, valLength);
|
|
str_length := valLength;
|
|
if str_length > fill_length then
|
|
str_lengthInBytes := fill_length
|
|
else
|
|
str_lengthInBytes := str_length;
|
|
MoveFast(valData^, Pointer(UIntPtr(targetData) + _start)^, str_lengthInBytes);
|
|
Need_start_fill := true;
|
|
end;
|
|
finally
|
|
cx.FreeRootedObject(valObj);
|
|
end;
|
|
end;
|
|
if not Need_start_fill then begin
|
|
// Then coerce everything that's not a string.
|
|
if not in_argv[1].isString then begin
|
|
valByte := in_argv[1].asInt64 and 255;
|
|
FillChar(Pointer(UIntPtr(targetData) + _start)^, fill_length, valByte);
|
|
Exit;
|
|
end;
|
|
|
|
if (argc > 4) and (in_argv[4].isString) then
|
|
encoding := ParseEncoding(in_argv[4].asJSString.ToUTF8(cx), UTF8)
|
|
else
|
|
encoding := UTF8;
|
|
|
|
str := in_argv[1].asJSString;
|
|
if encoding = UTF8 then begin
|
|
strUtf8 := str.ToUTF8(cx);
|
|
str_data := Pointer(strUtf8);
|
|
str_length := Length(strUtf8);
|
|
isLatin1 := True;
|
|
encoding := UTF8;
|
|
end else begin
|
|
isLatin1 := str.HasLatin1Chars;
|
|
if isLatin1 then begin
|
|
str_data := str.GetLatin1StringCharsAndLength(cx, str_length);
|
|
end else begin
|
|
str_data := str.GetTwoByteStringCharsAndLength(cx, str_length);
|
|
end;
|
|
end;
|
|
if encoding = UCS2 then
|
|
str_lengthInBytes := str_length * 2
|
|
else
|
|
str_lengthInBytes := str_length;
|
|
|
|
if str_lengthInBytes > fill_length then
|
|
str_lengthInBytes := fill_length;
|
|
|
|
case encoding of
|
|
UTF8: begin
|
|
if isLatin1 then
|
|
MoveFast(str_data^, Pointer(UIntPtr(targetData) + _start)^, str_lengthInBytes)
|
|
// else
|
|
// RawUnicodeToUtf8(Pointer(UIntPtr(targetData) + _start), str_length, str_data, str_length,[ccfNoTrailingZero]);
|
|
end;
|
|
UCS2: begin
|
|
if isLatin1 then begin
|
|
Latin1AnsiConvert.AnsiBufferToUnicode(Pointer(UIntPtr(targetData) + _start), str_data, str_lengthInBytes shr 1);
|
|
end else
|
|
MoveFast(str_data^, Pointer(UIntPtr(targetData) + _start)^, str_lengthInBytes);
|
|
end;
|
|
else begin
|
|
str_lengthInBytes := StringBytesWrite(Pointer(UIntPtr(targetData) + _start), fill_length, str_data, str_length, isLatin1, encoding);
|
|
if str_lengthInBytes = 0 then
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if str_lengthInBytes >= fill_length then
|
|
exit;
|
|
|
|
in_there := str_lengthInBytes;
|
|
ptr := Pointer(UIntPtr(targetData) + _start + str_lengthInBytes);
|
|
while (in_there < fill_length - in_there) do begin
|
|
MoveFast(Pointer(UIntPtr(targetData) + _start)^, ptr^, in_there);
|
|
ptr := Pointer(UIntPtr(ptr) + in_there);
|
|
in_there := in_there shl 1;
|
|
end;
|
|
|
|
if in_there < fill_length then
|
|
MoveFast(Pointer(UIntPtr(targetData) + _start)^, ptr^, fill_length - in_there);
|
|
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Computes the offset for starting an indexOf or lastIndexOf search.
|
|
// Returns either a valid offset in [0...<length - 1>], ie inside the Buffer,
|
|
// or -1 to signal that there is no possible match.
|
|
function IndexOfOffset(length: size_t; offset_i64: Int64; is_forward: Boolean): Int64; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
length_i64: Int64;
|
|
begin
|
|
length_i64 := length;
|
|
if length_i64 = 0 then
|
|
// Empty buffer, no match.
|
|
Result := -1
|
|
else if offset_i64 < 0 then begin
|
|
if offset_i64 + length_i64 >= 0 then
|
|
// Negative offsets count backwards from the end of the buffer.
|
|
Result := length_i64 + offset_i64
|
|
else if is_forward then
|
|
// indexOf from before the start of the buffer: search the whole buffer.
|
|
Result := 0
|
|
else
|
|
// lastIndexOf from before the start of the buffer: no match.
|
|
Result := -1
|
|
end else begin
|
|
if offset_i64 < length_i64 then
|
|
// Valid positive offset.
|
|
Result := offset_i64
|
|
else if is_forward then
|
|
// indexOf from past the end of the buffer: no match.
|
|
Result := -1
|
|
else
|
|
// lastIndexOf from past the end of the buffer: search the whole buffer.
|
|
Result := length_i64 - 1;
|
|
end;
|
|
end;
|
|
|
|
function SearchString(haystack: Pointer; haystack_length: size_t; needle: Pointer; needle_length: size_t; offset: size_t; is_forward: Boolean; isTwoByte: Boolean): size_t; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
pStart, pEnd: Pointer;
|
|
delta: integer;
|
|
begin;
|
|
// todo: optimize it
|
|
if is_forward then begin
|
|
pStart := @Puint8Vector(haystack)[offset];
|
|
pEnd := @Puint8Vector(haystack)[haystack_length - needle_length];
|
|
end else begin
|
|
// todo check is offset for start or for end in this case
|
|
if haystack_length - needle_length < offset then
|
|
offset := haystack_length - needle_length;
|
|
pStart := @Puint8Vector(haystack)[offset];
|
|
pEnd := haystack;
|
|
end;
|
|
|
|
if isTwoByte then
|
|
delta := 2
|
|
else
|
|
delta := 1;
|
|
|
|
Result := haystack_length;
|
|
|
|
while
|
|
(is_forward and (PtrUInt(pStart) <= PtrUInt(pEnd)))
|
|
or
|
|
(not is_forward and (PtrUInt(pEnd) <= PtrUInt(pStart)))
|
|
do begin
|
|
if CompareMem(pStart, needle, needle_length) then begin
|
|
Result := PtrUInt(pStart) - PtrUInt(haystack);
|
|
Break;
|
|
end;
|
|
if is_forward then
|
|
pStart := Pointer(PtrUInt(pStart) + delta)
|
|
else
|
|
pStart := Pointer(PtrUInt(pStart) - delta)
|
|
end;
|
|
|
|
end;
|
|
|
|
function indexOfBuffer(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
sourceObj: PJSRootedObject;
|
|
sourceData: Pointer;
|
|
sourceLength: size_t;
|
|
|
|
bufObj: PJSRootedObject;
|
|
bufData: Pointer;
|
|
bufLength: size_t;
|
|
|
|
offset_i64: Int64;
|
|
opt_offset: Int64;
|
|
offset: size_t;
|
|
encoding: TEncoding;
|
|
is_forward: Boolean;
|
|
|
|
haystack: Pointer;
|
|
haystack_length: size_t;
|
|
needle: Pointer;
|
|
needle_length: size_t;
|
|
|
|
res: size_t;
|
|
const
|
|
sInvalidCall = 'usage: indexOfBuffer(source, buf: Buffer; offset: Number; encoding: String; is_forward: boolean);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 5) or (not in_argv[0].isObject) or (not in_argv[1].isObject)
|
|
or (not in_argv[2].isNumber) or (not in_argv[4].isBoolean)
|
|
then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, -1);
|
|
|
|
sourceObj := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, sourceObj, sourceData, sourceLength);
|
|
finally
|
|
cx.FreeRootedObject(sourceObj);
|
|
end;
|
|
|
|
bufObj := cx.NewRootedObject(in_argv[1].asObject);
|
|
try
|
|
getBufDataAndLength(cx, bufObj, bufData, bufLength);
|
|
finally
|
|
cx.FreeRootedObject(bufObj);
|
|
end;
|
|
|
|
|
|
if in_argv[3].isString then
|
|
encoding := ParseEncoding(in_argv[3].asJSString.ToUTF8(cx), UTF8)
|
|
else
|
|
encoding := UTF8;
|
|
|
|
is_forward := in_argv[4].asBoolean;
|
|
|
|
haystack := sourceData;
|
|
haystack_length := sourceLength;
|
|
needle := bufData;
|
|
needle_length := bufLength;
|
|
|
|
if (needle_length = 0) or (haystack_length = 0) then
|
|
Exit;
|
|
|
|
offset_i64 := in_argv[2].asInt64;
|
|
|
|
opt_offset := IndexOfOffset(haystack_length, offset_i64, is_forward);
|
|
if opt_offset <= -1 then
|
|
Exit;
|
|
offset := opt_offset;
|
|
if offset >= haystack_length then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
if (is_forward and (needle_length + offset > haystack_length)) or (needle_length > haystack_length) then
|
|
Exit;
|
|
|
|
if encoding = UCS2 then begin
|
|
if (haystack_length < 2) or (needle_length < 2) then
|
|
Exit;
|
|
res := SearchString(haystack, haystack_length, needle, needle_length, offset, is_forward, true);
|
|
end else
|
|
res := SearchString(haystack, haystack_length, needle, needle_length, offset, is_forward, false);
|
|
if res = haystack_length then
|
|
Exit;
|
|
vp.rval := SimpleVariantToJSval(cx, res);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function indexOfNumber(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
sourceObj: PJSRootedObject;
|
|
sourceData: Pointer;
|
|
sourceLength: size_t;
|
|
|
|
needle: UInt32;
|
|
|
|
offset_i64: Int64;
|
|
opt_offset: Int64;
|
|
offset: size_t;
|
|
is_forward: Boolean;
|
|
|
|
res: size_t;
|
|
|
|
const
|
|
sInvalidCall = 'usage: indexOfNumber(source: Buffer; val, offset: Number; is_forward: boolean);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 3) or (not in_argv[0].isObject) or (not in_argv[1].isNumber) or (not in_argv[2].isNumber) or (not in_argv[3].isBoolean) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, -1);
|
|
sourceObj := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, sourceObj, sourceData, sourceLength);
|
|
finally
|
|
cx.FreeRootedObject(sourceObj);
|
|
end;
|
|
|
|
needle := in_argv[1].asInt64;
|
|
offset_i64 := in_argv[2].asInt64;
|
|
is_forward := in_argv[3].asBoolean;
|
|
|
|
opt_offset := IndexOfOffset(sourceLength, offset_i64, is_forward);
|
|
|
|
if opt_offset <= -1 then
|
|
Exit;
|
|
offset := opt_offset;
|
|
|
|
if offset >= sourceLength then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
//todo: optimize
|
|
res := SearchString(sourceData, sourceLength, @needle, 1, offset, is_forward, False);
|
|
if res = sourceLength then
|
|
Exit;
|
|
vp.rval := SimpleVariantToJSval(cx, res);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function indexOfString(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
sourceObj: PJSRootedObject;
|
|
sourceData: Pointer;
|
|
sourceLength: size_t;
|
|
|
|
// bufObj: PJSRootedObject;
|
|
// bufData: Pointer;
|
|
// bufLength: size_t;
|
|
|
|
offset_i64: Int64;
|
|
opt_offset: Int64;
|
|
offset: size_t;
|
|
encoding: TEncoding;
|
|
is_forward: Boolean;
|
|
|
|
haystack: Pointer;
|
|
haystack_length: size_t;
|
|
needle: PJSString;
|
|
needle_length: size_t;
|
|
needleData: Pointer;
|
|
isLatin1: Boolean;
|
|
needleUtf8: RawUTF8;
|
|
needleSynUnicode: SynUnicode;
|
|
|
|
res: size_t;
|
|
|
|
const
|
|
sInvalidCall = 'usage: indexOfString(source: Buffer; val: String; offset: Number; encoding: String; is_forward: boolean);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 5) or (not in_argv[0].isObject) or (not in_argv[1].isString) or (not in_argv[2].isNumber)
|
|
or (not in_argv[4].isBoolean) then
|
|
raise ESMException.Create(sInvalidCall);
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, -1);
|
|
if in_argv[3].isString then
|
|
encoding := ParseEncoding(in_argv[3].asJSString.ToUTF8(cx), UTF8)
|
|
else
|
|
encoding := UTF8;
|
|
|
|
sourceObj := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, sourceObj, sourceData, sourceLength);
|
|
finally
|
|
cx.FreeRootedObject(sourceObj);
|
|
end;
|
|
|
|
needle := in_argv[1].asJSString;
|
|
offset_i64 := in_argv[2].asInt64;
|
|
is_forward := in_argv[4].asBoolean;
|
|
|
|
haystack := sourceData;
|
|
// Round down to the nearest multiple of 2 in case of UCS2.
|
|
if encoding = UCS2 then
|
|
haystack_length := sourceLength and (not 1)
|
|
else
|
|
haystack_length := sourceLength;
|
|
|
|
isLatin1 := needle.HasLatin1Chars;
|
|
|
|
if isLatin1 then
|
|
needleData := needle.GetLatin1StringCharsAndLength(cx, needle_length)
|
|
else begin
|
|
needleData := needle.GetTwoByteStringCharsAndLength(cx, needle_length);
|
|
end;
|
|
if encoding = UCS2 then
|
|
needle_length := needle_length shl 1;
|
|
|
|
if (needle_length = 0) or (haystack_length = 0) then
|
|
Exit;
|
|
|
|
opt_offset := IndexOfOffset(haystack_length, offset_i64, is_forward);
|
|
if opt_offset <= -1 then
|
|
Exit;
|
|
offset := opt_offset;
|
|
if offset >= haystack_length then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
if (is_forward and (needle_length + offset > haystack_length)) or (needle_length > haystack_length) then
|
|
Exit;
|
|
|
|
res := haystack_length;
|
|
|
|
if encoding = UCS2 then begin
|
|
if (needleData = nil) or (haystack_length < 2) or (needle_length < 2) then
|
|
Exit;
|
|
if Endianness = kBigEndian then begin
|
|
raise ESMException.Create('Not implemented yet'); //todo
|
|
end else begin
|
|
if isLatin1 then
|
|
needleSynUnicode := needle.ToSynUnicode(cx)
|
|
else
|
|
SetString(needleSynUnicode, PWideChar(needleData), needle_length);
|
|
res := SearchString(haystack, haystack_length, Pointer(needleSynUnicode), Length(needleSynUnicode), offset, is_forward, true);
|
|
end;
|
|
end else if encoding = UTF8 then begin
|
|
//todo: optimize
|
|
needleUtf8 := needle.ToUTF8(cx);
|
|
if needleUtf8 = '' then
|
|
Exit;
|
|
res := SearchString(haystack, haystack_length, Pointer(needleUtf8), Length(needleUtf8), offset, is_forward, false);
|
|
end else if encoding = LATIN1 then begin
|
|
res := SearchString(haystack, haystack_length, needleData, needle_length, offset, is_forward, false);
|
|
end;
|
|
|
|
if res = haystack_length then
|
|
Exit;
|
|
|
|
vp.rval := SimpleVariantToJSval(cx, res);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure Swizzle(start: Pointer; len: size_t); {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
_start: PAnsiChar absolute start;
|
|
_end: PAnsiChar;
|
|
tmp: AnsiChar;
|
|
begin
|
|
_end := _start + (len - 1);
|
|
while (_start < _end) do begin
|
|
tmp := _start^;
|
|
_start^ := _end^;
|
|
_end^ := tmp;
|
|
inc(_start);
|
|
Dec(_end);
|
|
end;
|
|
end;
|
|
|
|
const
|
|
sFloatType: array[boolean] of RawUTF8 = ('Float', 'Double');
|
|
sFloatEndian: array[boolean] of RawUTF8 = ('LE', 'BE');
|
|
cFloatSize: array[boolean] of size_t = (4, 8);
|
|
|
|
function ReadFloatGeneric(cx: PJSContext; argc: uintN; var vp: jsargRec; endian: TEndianness; isDouble: Boolean): Boolean; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
bufObj: PJSRootedObject;
|
|
bufData: Pointer;
|
|
bufLength: size_t;
|
|
|
|
offset: size_t;
|
|
|
|
d: Double;
|
|
s: Single absolute d;
|
|
const
|
|
sInvalidCall = 'usage: read%%(buf: Buffer, offset: Number);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 2) or (not in_argv[0].isObject) or (not in_argv[1].isNumber) then
|
|
raise ESMException.CreateUTF8(sInvalidCall, [sFloatType[isDouble], sFloatEndian[endian = kLittleEndian]]);
|
|
|
|
bufObj := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, bufObj, bufData, bufLength);
|
|
finally
|
|
cx.FreeRootedObject(bufObj);
|
|
end;
|
|
|
|
offset := in_argv[1].asInt64;
|
|
if offset + cFloatSize[isDouble] > bufLength then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
|
|
MoveFast(Pointer(PtrUInt(bufData) + offset)^, d, cFloatSize[isDouble]);
|
|
|
|
if endian <> Endianness then
|
|
Swizzle(@d, cFloatSize[isDouble]);
|
|
|
|
if isDouble then
|
|
vp.rval := SimpleVariantToJSval(cx, d)
|
|
else
|
|
vp.rval := SimpleVariantToJSval(cx, s);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function readDoubleBE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := ReadFloatGeneric(cx, argc, vp, kBigEndian, true);
|
|
end;
|
|
|
|
function readDoubleLE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := ReadFloatGeneric(cx, argc, vp, kLittleEndian, true);
|
|
end;
|
|
|
|
function readFloatBE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := ReadFloatGeneric(cx, argc, vp, kBigEndian, False);
|
|
end;
|
|
|
|
function readFloatLE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := ReadFloatGeneric(cx, argc, vp, kLittleEndian, False);
|
|
end;
|
|
|
|
function WriteFloatGeneric(cx: PJSContext; argc: uintN; var vp: jsargRec; endian: TEndianness; isDouble: Boolean): Boolean; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
bufObj: PJSRootedObject;
|
|
bufData: Pointer;
|
|
bufLength: size_t;
|
|
|
|
offset: size_t;
|
|
should_assert: Boolean;
|
|
memcpy_num: size_t;
|
|
|
|
d: Double;
|
|
s: Single absolute d;
|
|
const
|
|
sInvalidCall = 'usage: Write%%(buf: Buffer; val: Number; offset: Number; [shouldAssert: boolean = false]);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 3) or (not in_argv[0].isObject) or (not in_argv[1].isNumber) or (not in_argv[2].isNumber) then
|
|
raise ESMException.CreateUTF8(sInvalidCall, [sFloatType[isDouble], sFloatEndian[endian = kLittleEndian]]);
|
|
|
|
should_assert := argc < 4;
|
|
bufObj := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, bufObj, bufData, bufLength);
|
|
finally
|
|
cx.FreeRootedObject(bufObj);
|
|
end;
|
|
|
|
if isDouble then
|
|
d := in_argv[1].asDouble
|
|
else
|
|
s := in_argv[1].asDouble;
|
|
|
|
offset := in_argv[2].asInt64;
|
|
|
|
if should_assert and ((offset + cFloatSize[isDouble] > bufLength) or (offset + cFloatSize[isDouble] < cFloatSize[isDouble]))then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
|
|
if (offset + cFloatSize[isDouble] > bufLength) then
|
|
memcpy_num := bufLength - offset
|
|
else
|
|
memcpy_num := cFloatSize[isDouble];
|
|
|
|
if endian <> Endianness then
|
|
Swizzle(@d, cFloatSize[isDouble]);
|
|
|
|
MoveFast(d, Pointer(PtrUInt(bufData) + offset)^, memcpy_num);
|
|
|
|
vp.rval := JSVAL_VOID;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function writeDoubleBE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := WriteFloatGeneric(cx, argc, vp, kBigEndian, True);
|
|
end;
|
|
|
|
function writeDoubleLE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := WriteFloatGeneric(cx, argc, vp, kLittleEndian, True);
|
|
end;
|
|
|
|
function writeFloatBE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := WriteFloatGeneric(cx, argc, vp, kBigEndian, False);
|
|
end;
|
|
|
|
function writeFloatLE(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := WriteFloatGeneric(cx, argc, vp, kLittleEndian, False);
|
|
end;
|
|
|
|
function swap(cx: PJSContext; argc: uintN; var vp: jsargRec; bytesCount: byte): Boolean; {$ifdef HASINLINE}inline;{$endif}
|
|
var
|
|
in_argv: PjsvalVector;
|
|
|
|
bufObj: PJSRootedObject;
|
|
bufData: Pointer;
|
|
bufLength: size_t;
|
|
|
|
i: size_t;
|
|
const
|
|
sInvalidCall = 'usage: swap%(buf: Buffer);';
|
|
begin
|
|
try
|
|
Result := True;
|
|
in_argv := vp.argv;
|
|
if (argc < 1) or (not in_argv[0].isObject)then
|
|
raise ESMException.CreateUTF8(sInvalidCall, [1 shl bytesCount]);
|
|
|
|
bufObj := cx.NewRootedObject(in_argv[0].asObject);
|
|
try
|
|
getBufDataAndLength(cx, bufObj, bufData, bufLength);
|
|
finally
|
|
cx.FreeRootedObject(bufObj);
|
|
end;
|
|
|
|
if bufLength mod bytesCount <> 0 then
|
|
raise ESMRangeException.Create(OUT_OF_RANGE);
|
|
|
|
i := 0;
|
|
while (i < bufLength) do begin
|
|
Swizzle(bufData, bytesCount);
|
|
bufData := Pointer(PtrUInt(bufData) + bytesCount);
|
|
Inc(i, bytesCount);
|
|
end;
|
|
|
|
vp.rval := in_argv[0];
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Result := False;
|
|
vp.rval := JSVAL_VOID;
|
|
JSError(cx, E);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function swap16(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := swap(cx, argc, vp, 2);
|
|
end;
|
|
|
|
function swap32(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := swap(cx, argc, vp, 4);
|
|
end;
|
|
|
|
function swap64(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
|
|
begin
|
|
Result := swap(cx, argc, vp, 8);
|
|
end;
|
|
|
|
const
|
|
BufferBinding_class: JSClass = (name: 'BufferBinding';
|
|
flags:
|
|
(1 shl JSCLASS_RESERVED_SLOTS_SHIFT);
|
|
);
|
|
|
|
function SyNodeBindingProc_buffer(const aEngine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;
|
|
var
|
|
obj: PJSRootedObject;
|
|
cx: PJSContext;
|
|
const
|
|
props = JSPROP_ENUMERATE or JSPROP_ENUMERATE or JSPROP_PERMANENT;
|
|
begin
|
|
cx := aEngine.cx;
|
|
obj := cx.NewRootedObject(cx.NewObject(@BufferBinding_class));
|
|
|
|
obj.ptr.DefineFunction(cx, 'setupBufferJS', setupBufferJS, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'createFromString', createFromString, 0, props);
|
|
|
|
obj.ptr.DefineFunction(cx, 'byteLengthUtf8', byteLengthUtf8, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'compare', compare, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'compareOffset', compareOffset, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'fill', fill, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'indexOfBuffer', indexOfBuffer, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'indexOfNumber', indexOfNumber, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'indexOfString', indexOfString, 0, props);
|
|
|
|
obj.ptr.DefineFunction(cx, 'readDoubleBE', readDoubleBE, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'readDoubleLE', readDoubleLE, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'readFloatBE', readFloatBE, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'readFloatLE', readFloatLE, 0, props);
|
|
|
|
obj.ptr.DefineFunction(cx, 'writeDoubleBE', writeDoubleBE, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'writeDoubleLE', writeDoubleLE, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'writeFloatBE', writeFloatBE, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'writeFloatLE', writeFloatLE, 0, props);
|
|
|
|
obj.ptr.DefineFunction(cx, 'swap16', swap16, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'swap32', swap32, 0, props);
|
|
obj.ptr.DefineFunction(cx, 'swap64', swap64, 0, props);
|
|
|
|
obj.ptr.DefineProperty(cx, 'kMaxLength', SimpleVariantToJSval(cx, kMaxLength), props);
|
|
obj.ptr.DefineProperty(cx, 'kStringMaxLength', SimpleVariantToJSval(cx, kMaxLength), props);
|
|
|
|
Result := obj.ptr.ToJSValue;
|
|
|
|
cx.FreeRootedObject(obj);
|
|
end;
|
|
|
|
initialization
|
|
if _Endianness.u16 = 1 then
|
|
Endianness := kLittleEndian
|
|
else
|
|
Endianness := kBigEndian;
|
|
|
|
TSMEngineManager.RegisterBinding('buffer', SyNodeBindingProc_buffer);
|
|
|
|
end.
|