mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-26 23:42:15 +00:00
slowly but steadily
This commit is contained in:
parent
8e9586783f
commit
36d54d63d5
@ -1,34 +1,20 @@
|
||||
// import {
|
||||
// MAX_BLENGTH,
|
||||
// HEADER_SIZE,
|
||||
// allocateUnsafe,
|
||||
// reallocateUnsafe,
|
||||
// LOAD,
|
||||
// STORE
|
||||
// } from "./internal/arraybuffer";
|
||||
|
||||
import {
|
||||
ALLOC,
|
||||
REALLOC,
|
||||
REGISTER,
|
||||
LINK,
|
||||
ArrayBufferView
|
||||
ArrayBufferView,
|
||||
FREE
|
||||
} from "./runtime";
|
||||
|
||||
import {
|
||||
ArrayBuffer
|
||||
} from "./arraybuffer";
|
||||
|
||||
// import {
|
||||
// allocateUnsafe as allocateUnsafeString,
|
||||
// freeUnsafe as freeUnsafeString,
|
||||
// copyUnsafe as copyUnsafeString
|
||||
// } from "./internal/string";
|
||||
|
||||
import {
|
||||
COMPARATOR,
|
||||
SORT
|
||||
} from "./internal/sort";
|
||||
} from "./util/sort";
|
||||
|
||||
import {
|
||||
itoa,
|
||||
@ -36,7 +22,7 @@ import {
|
||||
itoa_stream,
|
||||
dtoa_stream,
|
||||
MAX_DOUBLE_LENGTH
|
||||
} from "./internal/number";
|
||||
} from "./util/number";
|
||||
|
||||
import {
|
||||
isArray as builtin_isArray
|
||||
@ -372,186 +358,233 @@ export class Array<T> extends ArrayBufferView {
|
||||
}
|
||||
|
||||
reverse(): Array<T> {
|
||||
var buffer = this.buffer_;
|
||||
var base = this.dataStart;
|
||||
for (let front = 0, back = this.length_ - 1; front < back; ++front, --back) {
|
||||
let temp = LOAD<T>(buffer, front);
|
||||
STORE<T>(buffer, front, LOAD<T>(buffer, back));
|
||||
STORE<T>(buffer, back, temp);
|
||||
let temp: T = load<T>(base, front);
|
||||
store<T>(base + (<usize>front << alignof<T>()), load<T>(base + (<usize>back << alignof<T>())));
|
||||
store<T>(base + (<usize>back << alignof<T>()), temp);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
sort(comparator: (a: T, b: T) => i32 = COMPARATOR<T>()): this {
|
||||
// TODO remove this when flow will allow trackcing null
|
||||
// TODO remove this when flow will allow tracking null
|
||||
assert(comparator); // The comparison function must be a function
|
||||
|
||||
var length = this.length_;
|
||||
if (length <= 1) return this;
|
||||
var buffer = this.buffer_;
|
||||
var base = this.dataStart;
|
||||
if (length == 2) {
|
||||
let a = LOAD<T>(buffer, 1); // a = arr[1]
|
||||
let b = LOAD<T>(buffer, 0); // b = arr[0]
|
||||
let a: T = load<T>(base, sizeof<T>()); // a = arr[1]
|
||||
let b: T = load<T>(base); // b = arr[0]
|
||||
if (comparator(a, b) < 0) {
|
||||
STORE<T>(buffer, 1, b); // arr[1] = b;
|
||||
STORE<T>(buffer, 0, a); // arr[0] = a;
|
||||
store<T>(base, b, sizeof<T>()); // arr[1] = b;
|
||||
store<T>(base, a); // arr[0] = a;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
SORT<T>(buffer, 0, length, comparator);
|
||||
SORT<T>(base, length, comparator);
|
||||
return this;
|
||||
}
|
||||
|
||||
// FIXME: refactor into multiple functions?
|
||||
join(separator: string = ","): string {
|
||||
var lastIndex = this.length_ - 1;
|
||||
if (lastIndex < 0) return "";
|
||||
var result = "";
|
||||
var value: T;
|
||||
var buffer = this.buffer_;
|
||||
var base = this.dataStart;
|
||||
// var buffer = this.buffer_;
|
||||
var sepLen = separator.length;
|
||||
var hasSeparator = sepLen != 0;
|
||||
if (value instanceof bool) {
|
||||
if (!lastIndex) return select<string>("true", "false", LOAD<T,bool>(buffer, 0));
|
||||
if (!lastIndex) return select("true", "false", load<T>(base));
|
||||
|
||||
let valueLen = 5; // max possible length of element len("false")
|
||||
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
let result = allocateUnsafeString(estLen);
|
||||
let result = ALLOC(estLen << 1);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = LOAD<T,bool>(buffer, i);
|
||||
value = load<T>(base + i);
|
||||
valueLen = 4 + <i32>(!value);
|
||||
copyUnsafeString(result, offset, select<string>("true", "false", value), 0, valueLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(select("true", "false", value)),
|
||||
<usize>valueLen << 1
|
||||
);
|
||||
offset += valueLen;
|
||||
if (hasSeparator) {
|
||||
copyUnsafeString(result, offset, changetype<String>(separator), 0, sepLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(separator),
|
||||
<usize>sepLen << 1
|
||||
);
|
||||
offset += sepLen;
|
||||
}
|
||||
}
|
||||
value = LOAD<T,bool>(buffer, lastIndex);
|
||||
value = load<T>(base + <usize>lastIndex);
|
||||
valueLen = 4 + <i32>(!value);
|
||||
copyUnsafeString(result, offset, select<string>("true", "false", value), 0, valueLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(select("true", "false", value)),
|
||||
valueLen << 1
|
||||
);
|
||||
offset += valueLen;
|
||||
|
||||
let out = result;
|
||||
if (estLen > offset) {
|
||||
out = result.substring(0, offset);
|
||||
freeUnsafeString(result);
|
||||
let trimmed = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
return trimmed; // registered in .substring
|
||||
}
|
||||
return out;
|
||||
return REGISTER<string>(result);
|
||||
} else if (isInteger<T>()) {
|
||||
if (!lastIndex) return changetype<string>(itoa<T>(LOAD<T>(buffer, 0)));
|
||||
if (!lastIndex) return changetype<string>(itoa<T>(load<T>(base)));
|
||||
|
||||
const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>();
|
||||
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
let result = allocateUnsafeString(estLen);
|
||||
let result = ALLOC(estLen << 1);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = LOAD<T>(buffer, i);
|
||||
offset += itoa_stream<T>(changetype<usize>(result), offset, value);
|
||||
value = load<T>(base + (<usize>i << alignof<T>()));
|
||||
offset += itoa_stream<T>(result, offset, value);
|
||||
if (hasSeparator) {
|
||||
copyUnsafeString(result, offset, separator, 0, sepLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(separator),
|
||||
<usize>sepLen << 1
|
||||
);
|
||||
offset += sepLen;
|
||||
}
|
||||
}
|
||||
value = LOAD<T>(buffer, lastIndex);
|
||||
offset += itoa_stream<T>(changetype<usize>(result), offset, value);
|
||||
let out = result;
|
||||
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
|
||||
offset += itoa_stream<T>(result, offset, value);
|
||||
if (estLen > offset) {
|
||||
out = result.substring(0, offset);
|
||||
freeUnsafeString(result);
|
||||
let trimmed = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
return trimmed; // registered in .substring
|
||||
}
|
||||
return out;
|
||||
return REGISTER<string>(result);
|
||||
} else if (isFloat<T>()) {
|
||||
if (!lastIndex) return changetype<string>(dtoa(LOAD<T,f64>(buffer, 0)));
|
||||
if (!lastIndex) return changetype<string>(dtoa(load<T>(base)));
|
||||
|
||||
const valueLen = MAX_DOUBLE_LENGTH;
|
||||
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
let result = allocateUnsafeString(estLen);
|
||||
let result = ALLOC(estLen << 1);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = LOAD<T,f64>(buffer, i);
|
||||
offset += dtoa_stream(changetype<usize>(result), offset, value);
|
||||
value = load<T>(base + (<usize>i << alignof<T>()));
|
||||
offset += dtoa_stream(result, offset, value);
|
||||
if (hasSeparator) {
|
||||
copyUnsafeString(result, offset, separator, 0, sepLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(separator),
|
||||
<usize>sepLen << 1
|
||||
);
|
||||
offset += sepLen;
|
||||
}
|
||||
}
|
||||
value = LOAD<T,f64>(buffer, lastIndex);
|
||||
offset += dtoa_stream(changetype<usize>(result), offset, value);
|
||||
let out = result;
|
||||
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
|
||||
offset += dtoa_stream(result, offset, value);
|
||||
if (estLen > offset) {
|
||||
out = result.substring(0, offset);
|
||||
freeUnsafeString(result);
|
||||
let trimmed = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
return trimmed; // registered in .substring
|
||||
}
|
||||
return out;
|
||||
return REGISTER<string>(result);
|
||||
} else if (isString<T>()) {
|
||||
if (!lastIndex) return LOAD<string>(buffer, 0);
|
||||
if (!lastIndex) return load<string>(base);
|
||||
|
||||
let estLen = 0;
|
||||
for (let i = 0, len = lastIndex + 1; i < len; ++i) {
|
||||
estLen += LOAD<string>(buffer, i).length;
|
||||
estLen += load<string>(base + (<usize>i << alignof<T>())).length;
|
||||
}
|
||||
let offset = 0;
|
||||
let result = allocateUnsafeString(estLen + sepLen * lastIndex);
|
||||
let result = ALLOC((estLen + sepLen * lastIndex) << 1);
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = LOAD<string>(buffer, i);
|
||||
value = load<string>(base + (<usize>i << alignof<T>()));
|
||||
if (value) {
|
||||
let valueLen = value.length; // tslint:disable-line:no-unsafe-any
|
||||
copyUnsafeString(result, offset, value, 0, valueLen); // tslint:disable-line:no-unsafe-any
|
||||
offset += valueLen; // tslint:disable-line:no-unsafe-any
|
||||
let valueLen = changetype<string>(value).length;
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(value),
|
||||
<usize>valueLen << 1
|
||||
);
|
||||
offset += valueLen;
|
||||
}
|
||||
if (hasSeparator) {
|
||||
copyUnsafeString(result, offset, separator, 0, sepLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(separator),
|
||||
<usize>sepLen << 1
|
||||
);
|
||||
offset += sepLen;
|
||||
}
|
||||
}
|
||||
value = LOAD<string>(buffer, lastIndex);
|
||||
value = load<string>(base + (<usize>lastIndex << alignof<T>()));
|
||||
if (value) {
|
||||
let valueLen = value.length; // tslint:disable-line:no-unsafe-any
|
||||
copyUnsafeString(result, offset, value, 0, valueLen); // tslint:disable-line:no-unsafe-any
|
||||
let valueLen = changetype<string>(value).length;
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(value),
|
||||
<usize>valueLen << 1
|
||||
);
|
||||
}
|
||||
return result;
|
||||
return REGISTER<string>(result);
|
||||
} else if (isArray<T>()) {
|
||||
if (!lastIndex) {
|
||||
value = LOAD<T>(buffer, 0);
|
||||
return value ? value.join(separator) : ""; // tslint:disable-line:no-unsafe-any
|
||||
value = load<T>(base);
|
||||
return value ? value.join(separator) : "";
|
||||
}
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = LOAD<T>(buffer, i);
|
||||
if (value) result += value.join(separator); // tslint:disable-line:no-unsafe-any
|
||||
value = load<T>(base + (<usize>i << alignof<T>()));
|
||||
if (value) result += value.join(separator);
|
||||
if (hasSeparator) result += separator;
|
||||
}
|
||||
value = LOAD<T>(buffer, lastIndex);
|
||||
if (value) result += value.join(separator); // tslint:disable-line:no-unsafe-any
|
||||
return result;
|
||||
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
|
||||
if (value) result += value.join(separator);
|
||||
return result; // registered by concatenation (FIXME: lots of garbage)
|
||||
} else if (isReference<T>()) { // References
|
||||
if (!lastIndex) return "[object Object]";
|
||||
const valueLen = 15; // max possible length of element len("[object Object]")
|
||||
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
|
||||
let result = allocateUnsafeString(estLen);
|
||||
let result = ALLOC(estLen << 1);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < lastIndex; ++i) {
|
||||
value = LOAD<T>(buffer, i);
|
||||
value = load<T>(base + (<usize>i << alignof<T>()));
|
||||
if (value) {
|
||||
copyUnsafeString(result, offset, changetype<String>("[object Object]"), 0, valueLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>("[object Object]"),
|
||||
<usize>valueLen << 1
|
||||
);
|
||||
offset += valueLen;
|
||||
}
|
||||
if (hasSeparator) {
|
||||
copyUnsafeString(result, offset, changetype<String>(separator), 0, sepLen);
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>(separator),
|
||||
<usize>sepLen << 1
|
||||
);
|
||||
offset += sepLen;
|
||||
}
|
||||
}
|
||||
if (LOAD<T>(buffer, lastIndex)) {
|
||||
copyUnsafeString(result, offset, changetype<String>("[object Object]"), 0, valueLen);
|
||||
if (load<T>(base + (<usize>lastIndex << alignof<T>()))) {
|
||||
memory.copy(
|
||||
result + (<usize>offset << 1),
|
||||
changetype<usize>("[object Object]"),
|
||||
<usize>valueLen << 1
|
||||
);
|
||||
offset += valueLen;
|
||||
}
|
||||
let out = result;
|
||||
if (estLen > offset) {
|
||||
out = result.substring(0, offset);
|
||||
freeUnsafeString(result);
|
||||
let out = changetype<string>(result).substring(0, offset);
|
||||
FREE(result);
|
||||
return out; // registered in .substring
|
||||
}
|
||||
return out;
|
||||
return REGISTER<string>(result);
|
||||
} else {
|
||||
assert(false); // Unsupported generic typename
|
||||
ERROR("unspported type");
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,16 +593,16 @@ export class Array<T> extends ArrayBufferView {
|
||||
return this.join();
|
||||
}
|
||||
|
||||
private __gc(): void {
|
||||
var buffer = this.buffer_;
|
||||
__gc_mark(changetype<usize>(buffer)); // tslint:disable-line
|
||||
if (isManaged<T>()) {
|
||||
let offset: usize = 0;
|
||||
let end = <usize>this.length_ << alignof<usize>();
|
||||
while (offset < end) {
|
||||
__gc_mark(load<usize>(changetype<usize>(buffer) + offset, HEADER_SIZE)); // tslint:disable-line
|
||||
offset += sizeof<usize>();
|
||||
}
|
||||
}
|
||||
}
|
||||
// private __gc(): void {
|
||||
// var buffer = this.buffer_;
|
||||
// __gc_mark(changetype<usize>(buffer)); // tslint:disable-line
|
||||
// if (isManaged<T>()) {
|
||||
// let offset: usize = 0;
|
||||
// let end = <usize>this.length_ << alignof<usize>();
|
||||
// while (offset < end) {
|
||||
// __gc_mark(load<usize>(changetype<usize>(buffer) + offset, HEADER_SIZE)); // tslint:disable-line
|
||||
// offset += sizeof<usize>();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
@ -1,84 +0,0 @@
|
||||
import {
|
||||
AL_MASK,
|
||||
MAX_SIZE_32
|
||||
} from "./allocator";
|
||||
|
||||
/** Size of an ArrayBuffer header. */
|
||||
@inline export const HEADER_SIZE: usize = (offsetof<ArrayBuffer>() + AL_MASK) & ~AL_MASK;
|
||||
/** Maximum byte length of an ArrayBuffer. */
|
||||
@inline export const MAX_BLENGTH: i32 = <i32>MAX_SIZE_32 - HEADER_SIZE;
|
||||
|
||||
function computeSize(byteLength: i32): usize {
|
||||
// round up to power of 2, with HEADER_SIZE=8:
|
||||
// 0 -> 2^3 = 8
|
||||
// 1..8 -> 2^4 = 16
|
||||
// 9..24 -> 2^5 = 32
|
||||
// ...
|
||||
// MAX_LENGTH -> 2^30 = 0x40000000 (MAX_SIZE_32)
|
||||
return <usize>1 << <usize>(<u32>32 - clz<u32>(byteLength + HEADER_SIZE - 1));
|
||||
}
|
||||
|
||||
// Low-level utility
|
||||
|
||||
function __gc(ref: usize): void {}
|
||||
|
||||
export function allocateUnsafe(byteLength: i32): ArrayBuffer {
|
||||
assert(<u32>byteLength <= <u32>MAX_BLENGTH);
|
||||
var buffer: usize;
|
||||
if (isManaged<ArrayBuffer>()) {
|
||||
buffer = __gc_allocate(computeSize(byteLength), __gc); // tslint:disable-line
|
||||
} else {
|
||||
buffer = memory.allocate(computeSize(byteLength));
|
||||
}
|
||||
store<i32>(buffer, byteLength, offsetof<ArrayBuffer>("byteLength"));
|
||||
return changetype<ArrayBuffer>(buffer);
|
||||
}
|
||||
|
||||
export function reallocateUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuffer {
|
||||
var oldByteLength = buffer.byteLength;
|
||||
if (newByteLength > oldByteLength) {
|
||||
assert(newByteLength <= MAX_BLENGTH);
|
||||
if (newByteLength <= <i32>(computeSize(oldByteLength) - HEADER_SIZE)) { // fast path: zero out additional space
|
||||
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
|
||||
} else { // slow path: copy to new buffer
|
||||
let newBuffer = allocateUnsafe(newByteLength);
|
||||
memory.copy(
|
||||
changetype<usize>(newBuffer) + HEADER_SIZE,
|
||||
changetype<usize>(buffer) + HEADER_SIZE,
|
||||
<usize>oldByteLength
|
||||
);
|
||||
if (!isManaged<ArrayBuffer>()) {
|
||||
memory.free(changetype<usize>(buffer));
|
||||
}
|
||||
buffer = newBuffer;
|
||||
}
|
||||
memory.fill(
|
||||
changetype<usize>(buffer) + HEADER_SIZE + <usize>oldByteLength,
|
||||
0,
|
||||
<usize>(newByteLength - oldByteLength)
|
||||
);
|
||||
} else if (newByteLength < oldByteLength) { // fast path: override size
|
||||
// TBD: worth to copy and release if size is significantly less than before?
|
||||
assert(newByteLength >= 0);
|
||||
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// The helpers below use two different types in order to emit loads and stores that load respectively
|
||||
// store one type to/from memory while returning/taking the desired output/input type. This allows to
|
||||
// emit instructions like
|
||||
//
|
||||
// * `i32.load8` ^= `<i32>load<i8>(...)` that reads an i8 but returns an i32, or
|
||||
// * `i64.load32_s` ^= `<i64>load<i32>(...)`) that reads a 32-bit as a 64-bit integer
|
||||
//
|
||||
// without having to emit an additional instruction for conversion purposes. The second parameter
|
||||
// can be omitted for references and other loads and stores that simply return the exact type.
|
||||
|
||||
@inline export function LOAD<T,TOut = T>(buffer: ArrayBuffer, index: i32, byteOffset: i32 = 0): TOut {
|
||||
return <TOut>load<T>(changetype<usize>(buffer) + (<usize>index << alignof<T>()) + <usize>byteOffset, HEADER_SIZE);
|
||||
}
|
||||
|
||||
@inline export function STORE<T,TIn = T>(buffer: ArrayBuffer, index: i32, value: TIn, byteOffset: i32 = 0): void {
|
||||
store<T>(changetype<usize>(buffer) + (<usize>index << alignof<T>()) + <usize>byteOffset, value, HEADER_SIZE);
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
export function compareImpl(str1: String, index1: usize, str2: String, index2: usize, len: usize): i32 {
|
||||
var result: i32 = 0;
|
||||
var ptr1 = changetype<usize>(str1) + (index1 << 1);
|
||||
var ptr2 = changetype<usize>(str2) + (index2 << 1);
|
||||
while (len && !(result = <i32>load<u16>(ptr1) - <i32>load<u16>(ptr2))) {
|
||||
--len, ptr1 += 2, ptr2 += 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
import { MAX_SIZE_32 } from "./allocator";
|
||||
import { String } from "../string";
|
||||
|
||||
/** Size of a String header. */
|
||||
@inline export const HEADER_SIZE = (offsetof<String>() + 1) & ~1; // 2 byte aligned
|
||||
/** Maximum length of a String. */
|
||||
@inline export const MAX_LENGTH = (<i32>MAX_SIZE_32 - HEADER_SIZE) >>> 1;
|
||||
|
||||
// Low-level utility
|
||||
|
||||
function __gc(ref: usize): void {}
|
||||
|
||||
export function allocateUnsafe(length: i32): String {
|
||||
assert(length > 0 && length <= MAX_LENGTH);
|
||||
var buffer: usize;
|
||||
if (isManaged<String>()) {
|
||||
buffer = __gc_allocate(HEADER_SIZE + (<usize>length << 1), __gc); // tslint:disable-line
|
||||
} else {
|
||||
buffer = memory.allocate(HEADER_SIZE + (<usize>length << 1));
|
||||
}
|
||||
store<i32>(buffer, length);
|
||||
return changetype<String>(buffer);
|
||||
}
|
||||
|
||||
@inline
|
||||
export function freeUnsafe(buffer: String): void {
|
||||
if (!isManaged<String>()) {
|
||||
assert(buffer);
|
||||
memory.free(changetype<usize>(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
export function copyUnsafe(dest: String, destOffset: usize, src: String, srcOffset: usize, len: usize): void {
|
||||
memory.copy(
|
||||
changetype<usize>(dest) + (destOffset << 1) + HEADER_SIZE,
|
||||
changetype<usize>(src) + (srcOffset << 1) + HEADER_SIZE,
|
||||
len << 1
|
||||
);
|
||||
}
|
||||
|
||||
export function repeatUnsafe(dest: String, destOffset: usize, src: String, count: i32): void {
|
||||
var length = src.length;
|
||||
if (ASC_SHRINK_LEVEL > 1) {
|
||||
let strLen = length << 1;
|
||||
let to = changetype<usize>(dest) + HEADER_SIZE + (destOffset << 1);
|
||||
let from = changetype<usize>(src) + HEADER_SIZE;
|
||||
for (let i = 0, len = strLen * count; i < len; i += strLen) {
|
||||
memory.copy(to + i, from, strLen);
|
||||
}
|
||||
} else {
|
||||
switch (length) {
|
||||
case 0: break;
|
||||
case 1: {
|
||||
let cc = load<u16>(changetype<usize>(src), HEADER_SIZE);
|
||||
let out = changetype<usize>(dest) + (destOffset << 1);
|
||||
for (let i = 0; i < count; ++i) {
|
||||
store<u16>(out + (i << 1), cc, HEADER_SIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
let cc = load<u32>(changetype<usize>(src), HEADER_SIZE);
|
||||
let out = changetype<usize>(dest) + (destOffset << 1);
|
||||
for (let i = 0; i < count; ++i) {
|
||||
store<u32>(out + (i << 2), cc, HEADER_SIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
let cc1 = load<u32>(changetype<usize>(src), HEADER_SIZE + 0);
|
||||
let cc2 = load<u16>(changetype<usize>(src), HEADER_SIZE + 4);
|
||||
let out = changetype<usize>(dest) + (destOffset << 1);
|
||||
for (let i = 0; i < count; ++i) {
|
||||
store<u32>(out + (i << 2), cc1, HEADER_SIZE + 0);
|
||||
store<u16>(out + (i << 1), cc2, HEADER_SIZE + 4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
let cc = load<u64>(changetype<usize>(src), HEADER_SIZE);
|
||||
let out = changetype<usize>(dest) + (destOffset << 1);
|
||||
for (let i = 0; i < count; ++i) {
|
||||
store<u64>(out + (i << 3), cc, HEADER_SIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
let strLen = length << 1;
|
||||
let to = changetype<usize>(dest) + HEADER_SIZE + (destOffset << 1);
|
||||
let from = changetype<usize>(src) + HEADER_SIZE;
|
||||
for (let i = 0, len = strLen * count; i < len; i += strLen) {
|
||||
memory.copy(to + i, from, strLen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
@inline export const enum CharCode {
|
||||
PLUS = 0x2B,
|
||||
MINUS = 0x2D,
|
||||
DOT = 0x2E,
|
||||
_0 = 0x30,
|
||||
_1 = 0x31,
|
||||
_2 = 0x32,
|
||||
_3 = 0x33,
|
||||
_4 = 0x34,
|
||||
_5 = 0x35,
|
||||
_6 = 0x36,
|
||||
_7 = 0x37,
|
||||
_8 = 0x38,
|
||||
_9 = 0x39,
|
||||
A = 0x41,
|
||||
B = 0x42,
|
||||
E = 0x45,
|
||||
N = 0x4E,
|
||||
O = 0x4F,
|
||||
X = 0x58,
|
||||
Z = 0x5a,
|
||||
a = 0x61,
|
||||
b = 0x62,
|
||||
e = 0x65,
|
||||
n = 0x6E,
|
||||
o = 0x6F,
|
||||
x = 0x78,
|
||||
z = 0x7A
|
||||
}
|
||||
|
||||
export function isWhiteSpaceOrLineTerminator(c: u16): bool {
|
||||
switch (c) {
|
||||
case 9: // <TAB>
|
||||
case 10: // <LF>
|
||||
case 13: // <CR>
|
||||
case 11: // <VT>
|
||||
case 12: // <FF>
|
||||
case 32: // <SP>
|
||||
case 160: // <NBSP>
|
||||
case 8232: // <LS>
|
||||
case 8233: // <PS>
|
||||
case 65279: return true; // <ZWNBSP>
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Parses a string to an integer (usually), using the specified radix. */
|
||||
export function parse<T>(str: String, radix: i32 = 0): T {
|
||||
var len: i32 = str.length;
|
||||
if (!len) return <T>NaN;
|
||||
|
||||
var ptr = changetype<usize>(str) /* + HEAD -> offset */;
|
||||
var code = <i32>load<u16>(ptr, HEADER_SIZE);
|
||||
|
||||
// determine sign
|
||||
var sign: T;
|
||||
if (code == CharCode.MINUS) {
|
||||
if (!--len) return <T>NaN;
|
||||
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
|
||||
sign = -1;
|
||||
} else if (code == CharCode.PLUS) {
|
||||
if (!--len) return <T>NaN;
|
||||
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
|
||||
sign = 1;
|
||||
} else {
|
||||
sign = 1;
|
||||
}
|
||||
|
||||
// determine radix
|
||||
if (!radix) {
|
||||
if (code == CharCode._0 && len > 2) {
|
||||
switch (<i32>load<u16>(ptr + 2, HEADER_SIZE)) {
|
||||
case CharCode.B:
|
||||
case CharCode.b: {
|
||||
ptr += 4; len -= 2;
|
||||
radix = 2;
|
||||
break;
|
||||
}
|
||||
case CharCode.O:
|
||||
case CharCode.o: {
|
||||
ptr += 4; len -= 2;
|
||||
radix = 8;
|
||||
break;
|
||||
}
|
||||
case CharCode.X:
|
||||
case CharCode.x: {
|
||||
ptr += 4; len -= 2;
|
||||
radix = 16;
|
||||
break;
|
||||
}
|
||||
default: radix = 10;
|
||||
}
|
||||
} else radix = 10;
|
||||
} else if (radix < 2 || radix > 36) {
|
||||
return <T>NaN;
|
||||
}
|
||||
|
||||
// calculate value
|
||||
var num: T = 0;
|
||||
while (len--) {
|
||||
code = <i32>load<u16>(ptr, HEADER_SIZE);
|
||||
if (code >= CharCode._0 && code <= CharCode._9) {
|
||||
code -= CharCode._0;
|
||||
} else if (code >= CharCode.A && code <= CharCode.Z) {
|
||||
code -= CharCode.A - 10;
|
||||
} else if (code >= CharCode.a && code <= CharCode.z) {
|
||||
code -= CharCode.a - 10;
|
||||
} else break;
|
||||
if (code >= radix) break;
|
||||
num = (num * radix) + code;
|
||||
ptr += 2;
|
||||
}
|
||||
return sign * num;
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
import {
|
||||
ALLOC_RAW,
|
||||
REGISTER,
|
||||
LINK
|
||||
} from "../runtime";
|
||||
|
||||
import {
|
||||
ArrayBuffer
|
||||
} from "../arraybuffer";
|
||||
|
||||
import {
|
||||
SORT as SORT_IMPL
|
||||
} from "./sort";
|
||||
|
||||
/** Typed array base class. Not a global object. */
|
||||
export abstract class TypedArray<T> {
|
||||
[key: number]: T; // compatibility only
|
||||
|
||||
readonly buffer: ArrayBuffer;
|
||||
readonly byteOffset: i32;
|
||||
readonly byteLength: i32;
|
||||
|
||||
constructor(length: i32) {
|
||||
const MAX_LENGTH = <u32>ArrayBuffer.MAX_BYTELENGTH / sizeof<T>();
|
||||
if (<u32>length > MAX_LENGTH) throw new RangeError("Invalid typed array length");
|
||||
var byteLength = length << alignof<T>();
|
||||
this.buffer = new ArrayBuffer(byteLength);
|
||||
this.byteOffset = 0;
|
||||
this.byteLength = byteLength;
|
||||
}
|
||||
|
||||
get length(): i32 {
|
||||
return this.byteLength >>> alignof<T>();
|
||||
}
|
||||
|
||||
// TODO: could compute load/store offset from index and emit an immediate -> make this a builtin?
|
||||
|
||||
@operator("[]") protected __get(index: i32): T {
|
||||
if (<u32>index >= <u32>(this.byteLength >>> alignof<T>())) throw new Error("Index out of bounds");
|
||||
return load<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset, <usize>index << alignof<T>());
|
||||
}
|
||||
|
||||
@inline @operator("{}") protected __unchecked_get(index: i32): T {
|
||||
return load<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset + (<usize>index << alignof<T>()));
|
||||
}
|
||||
|
||||
@operator("[]=") protected __set(index: i32, value: native<T>): void {
|
||||
if (<u32>index >= <u32>(this.byteLength >>> alignof<T>())) throw new Error("Index out of bounds");
|
||||
store<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset + (<usize>index << alignof<T>()), value);
|
||||
}
|
||||
|
||||
@inline @operator("{}=") protected __unchecked_set(index: i32, value: native<T>): void {
|
||||
store<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset + (<usize>index << alignof<T>()), value);
|
||||
}
|
||||
|
||||
// copyWithin(target: i32, start: i32, end: i32 = this.length): this
|
||||
}
|
||||
|
||||
@inline export function FILL<TArray extends TypedArray<T>, T extends number>(
|
||||
array: TArray,
|
||||
value: native<T>,
|
||||
start: i32,
|
||||
end: i32
|
||||
): TArray {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
var len = array.length;
|
||||
start = start < 0 ? max(len + start, 0) : min(start, len);
|
||||
end = end < 0 ? max(len + end, 0) : min(end, len);
|
||||
if (sizeof<T>() == 1) {
|
||||
if (start < end) memory.fill(base + <usize>start, <u8>value, <usize>(end - start));
|
||||
} else {
|
||||
for (; start < end; ++start) {
|
||||
store<T>(base + (<usize>start << alignof()), value);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@inline export function SORT<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
comparator: (a: T, b: T) => i32
|
||||
): TArray {
|
||||
var length = array.length;
|
||||
var offset = <usize>array.byteOffset;
|
||||
if (length <= 1) return array;
|
||||
var buffer = changetype<usize>(array.buffer);
|
||||
if (length == 2) {
|
||||
let a = load<T>(buffer + offset, sizeof<T>());
|
||||
let b = load<T>(buffer + offset);
|
||||
if (comparator(a, b) < 0) {
|
||||
store<T>(buffer + offset, b, sizeof<T>());
|
||||
store<T>(buffer + offset, a);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
// TODO
|
||||
// SORT_IMPL<T>(buffer, byteOffset, length, comparator);
|
||||
return array;
|
||||
}
|
||||
|
||||
@inline export function SUBARRAY<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
begin: i32,
|
||||
end: i32
|
||||
): TArray {
|
||||
var buffer = changetype<usize>(array.buffer);
|
||||
var length = <i32>array.length;
|
||||
if (begin < 0) begin = max(length + begin, 0);
|
||||
else begin = min(begin, length);
|
||||
if (end < 0) end = max(length + end, begin);
|
||||
else end = max(min(end, length), begin);
|
||||
var out = ALLOC_RAW(offsetof<TArray>());
|
||||
store<usize>(out, buffer, offsetof<TArray>("buffer"));
|
||||
store<usize>(out, buffer + (<usize>begin << alignof<T>()), offsetof<TArray>("dataStart"));
|
||||
store<usize>(out, buffer + (<usize>end << alignof<T>()), offsetof<TArray>("dataStart"));
|
||||
store<i32>(out, <i32>array.byteOffset + (begin << alignof<T>()), offsetof<TArray>("byteOffset"));
|
||||
store<i32>(out, (end - begin) << alignof<T>(), offsetof<TArray>("byteLength"));
|
||||
LINK(buffer, REGISTER<TArray>(out)); // register first, then link
|
||||
return changetype<TArray>(out);
|
||||
}
|
||||
|
||||
@inline export function REDUCE<TArray extends TypedArray<T>, T, TRet>(
|
||||
array: TArray,
|
||||
callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
|
||||
initialValue: TRet
|
||||
): TRet {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
initialValue = callbackfn(initialValue, load<T>(base + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
@inline export function REDUCE_RIGHT<TArray extends TypedArray<T>, T, TRet>(
|
||||
array: TArray,
|
||||
callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
|
||||
initialValue: TRet
|
||||
): TRet {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
initialValue = callbackfn(initialValue, load<T>(base + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
@inline export function MAP<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, self: TArray) => T,
|
||||
): TArray {
|
||||
var length = array.length;
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
var result = instantiate<TArray>(length);
|
||||
var resultBase = changetype<usize>(result.buffer); // assumes byteOffset = 0
|
||||
for (let i = 0; i < length; i++) {
|
||||
store<T>(
|
||||
resultBase + (<usize>i << alignof<T>()),
|
||||
callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@inline export function FIND_INDEX<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): i32 {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
if (callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@inline export function SOME<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): bool {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
if (callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@inline export function EVERY<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): bool {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
if (callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)) continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@inline export function FOREACH<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => void,
|
||||
): void {
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
}
|
@ -355,7 +355,8 @@ import {
|
||||
// split by chars
|
||||
length = min<isize>(length, <isize>limit);
|
||||
let result = new Array<String>(length);
|
||||
let buffer = <ArrayBuffer>result.buffer_;
|
||||
let buffer = unreachable(); // TODO
|
||||
// let buffer = <ArrayBuffer>result.buffer_;
|
||||
for (let i: isize = 0; i < length; ++i) {
|
||||
let char = ALLOC(2);
|
||||
store<u16>(
|
||||
|
@ -1,11 +1,4 @@
|
||||
import {
|
||||
HEADER_SIZE
|
||||
} from "./string";
|
||||
|
||||
/** Computes the 32-bit hash of a value of any type. */
|
||||
@inline
|
||||
export function HASH<T>(key: T): u32 {
|
||||
// branch-level tree-shaking makes this a `(return (call ...))`
|
||||
@inline export function HASH<T>(key: T): u32 {
|
||||
if (isString(key)) {
|
||||
return hashStr(key);
|
||||
} else if (isReference<T>()) {
|
||||
@ -66,7 +59,7 @@ function hash64(key: u64): u32 {
|
||||
function hashStr(key: string): u32 {
|
||||
var v = FNV_OFFSET;
|
||||
for (let i: usize = 0, k: usize = key.length << 1; i < k; ++i) {
|
||||
v = (v ^ <u32>load<u8>(changetype<usize>(key) + i, HEADER_SIZE)) * FNV_PRIME;
|
||||
v = (v ^ <u32>load<u8>(changetype<usize>(key) + i)) * FNV_PRIME;
|
||||
}
|
||||
return v;
|
||||
}
|
@ -8,10 +8,6 @@ import {
|
||||
FREE
|
||||
} from "../runtime";
|
||||
|
||||
import {
|
||||
LOAD
|
||||
} from "./arraybuffer";
|
||||
|
||||
@inline export const MAX_DOUBLE_LENGTH = 28;
|
||||
|
||||
@lazy @inline const POWERS10: u32[] = [
|
@ -1,6 +1,4 @@
|
||||
/** Obtains the default comparator for the specified value type. */
|
||||
@inline
|
||||
export function COMPARATOR<T>(): (a: T, b: T) => i32 {
|
||||
@inline export function COMPARATOR<T>(): (a: T, b: T) => i32 {
|
||||
if (isInteger<T>()) {
|
||||
if (isSigned<T>() && sizeof<T>() <= 4) {
|
||||
return (a: T, b: T): i32 => (<i32>(a - b));
|
||||
@ -40,8 +38,7 @@ export function COMPARATOR<T>(): (a: T, b: T) => i32 {
|
||||
}
|
||||
}
|
||||
|
||||
@inline
|
||||
export function SORT<T>(
|
||||
@inline export function SORT<T>(
|
||||
dataStart: usize,
|
||||
length: i32,
|
||||
comparator: (a: T, b: T) => i32
|
||||
@ -58,7 +55,6 @@ export function SORT<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/** Sorts an Array with the 'Insertion Sort' algorithm. */
|
||||
function insertionSort<T>(
|
||||
dataStart: usize,
|
||||
length: i32,
|
||||
@ -77,7 +73,6 @@ function insertionSort<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/** Sorts an Array with the 'Weak Heap Sort' algorithm. */
|
||||
function weakHeapSort<T>(
|
||||
dataStart: usize,
|
||||
length: i32,
|
124
std/assembly/util/string.ts
Normal file
124
std/assembly/util/string.ts
Normal file
@ -0,0 +1,124 @@
|
||||
export function compareImpl(str1: string, index1: usize, str2: string, index2: usize, len: usize): i32 {
|
||||
var result: i32 = 0;
|
||||
var ptr1 = changetype<usize>(str1) + (index1 << 1);
|
||||
var ptr2 = changetype<usize>(str2) + (index2 << 1);
|
||||
while (len && !(result = <i32>load<u16>(ptr1) - <i32>load<u16>(ptr2))) {
|
||||
--len, ptr1 += 2, ptr2 += 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@inline export const enum CharCode {
|
||||
PLUS = 0x2B,
|
||||
MINUS = 0x2D,
|
||||
DOT = 0x2E,
|
||||
_0 = 0x30,
|
||||
_1 = 0x31,
|
||||
_2 = 0x32,
|
||||
_3 = 0x33,
|
||||
_4 = 0x34,
|
||||
_5 = 0x35,
|
||||
_6 = 0x36,
|
||||
_7 = 0x37,
|
||||
_8 = 0x38,
|
||||
_9 = 0x39,
|
||||
A = 0x41,
|
||||
B = 0x42,
|
||||
E = 0x45,
|
||||
N = 0x4E,
|
||||
O = 0x4F,
|
||||
X = 0x58,
|
||||
Z = 0x5a,
|
||||
a = 0x61,
|
||||
b = 0x62,
|
||||
e = 0x65,
|
||||
n = 0x6E,
|
||||
o = 0x6F,
|
||||
x = 0x78,
|
||||
z = 0x7A
|
||||
}
|
||||
|
||||
export function isWhiteSpaceOrLineTerminator(c: u16): bool {
|
||||
switch (c) {
|
||||
case 9: // <TAB>
|
||||
case 10: // <LF>
|
||||
case 13: // <CR>
|
||||
case 11: // <VT>
|
||||
case 12: // <FF>
|
||||
case 32: // <SP>
|
||||
case 160: // <NBSP>
|
||||
case 8232: // <LS>
|
||||
case 8233: // <PS>
|
||||
case 65279: return true; // <ZWNBSP>
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Parses a string to an integer (usually), using the specified radix. */
|
||||
export function parse<T>(str: string, radix: i32 = 0): T {
|
||||
var len: i32 = str.length;
|
||||
if (!len) return <T>NaN;
|
||||
|
||||
var ptr = changetype<usize>(str) /* + HEAD -> offset */;
|
||||
var code = <i32>load<u16>(ptr, HEADER_SIZE);
|
||||
|
||||
// determine sign
|
||||
var sign: T;
|
||||
if (code == CharCode.MINUS) {
|
||||
if (!--len) return <T>NaN;
|
||||
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
|
||||
sign = -1;
|
||||
} else if (code == CharCode.PLUS) {
|
||||
if (!--len) return <T>NaN;
|
||||
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
|
||||
sign = 1;
|
||||
} else {
|
||||
sign = 1;
|
||||
}
|
||||
|
||||
// determine radix
|
||||
if (!radix) {
|
||||
if (code == CharCode._0 && len > 2) {
|
||||
switch (<i32>load<u16>(ptr + 2, HEADER_SIZE)) {
|
||||
case CharCode.B:
|
||||
case CharCode.b: {
|
||||
ptr += 4; len -= 2;
|
||||
radix = 2;
|
||||
break;
|
||||
}
|
||||
case CharCode.O:
|
||||
case CharCode.o: {
|
||||
ptr += 4; len -= 2;
|
||||
radix = 8;
|
||||
break;
|
||||
}
|
||||
case CharCode.X:
|
||||
case CharCode.x: {
|
||||
ptr += 4; len -= 2;
|
||||
radix = 16;
|
||||
break;
|
||||
}
|
||||
default: radix = 10;
|
||||
}
|
||||
} else radix = 10;
|
||||
} else if (radix < 2 || radix > 36) {
|
||||
return <T>NaN;
|
||||
}
|
||||
|
||||
// calculate value
|
||||
var num: T = 0;
|
||||
while (len--) {
|
||||
code = <i32>load<u16>(ptr, HEADER_SIZE);
|
||||
if (code >= CharCode._0 && code <= CharCode._9) {
|
||||
code -= CharCode._0;
|
||||
} else if (code >= CharCode.A && code <= CharCode.Z) {
|
||||
code -= CharCode.A - 10;
|
||||
} else if (code >= CharCode.a && code <= CharCode.z) {
|
||||
code -= CharCode.a - 10;
|
||||
} else break;
|
||||
if (code >= radix) break;
|
||||
num = (num * radix) + code;
|
||||
ptr += 2;
|
||||
}
|
||||
return sign * num;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user