import { OBJECT, TOTAL_OVERHEAD } from "rt/common"; import { bs } from "../../../lib/as-bs"; import { BACK_SLASH } from "../../custom/chars"; import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables"; import { u16_to_hex4_swar } from "../../util/swar"; // @ts-expect-error: @lazy is a valid decorator @lazy const U00_MARKER = 13511005048209500; // @ts-expect-error: @lazy is a valid decorator @lazy const U_MARKER = 7667804; // @ts-expect-error: @lazy is a valid decorator @lazy const SPLAT_0022 = i16x8.splat(0x0022); // " // @ts-expect-error: @lazy is a valid decorator @lazy const SPLAT_005C = i16x8.splat(0x005c); // \ // @ts-expect-error: @lazy is a valid decorator @lazy const SPLAT_0020 = i16x8.splat(0x0020); // space and control check // @ts-expect-error: @lazy is a valid decorator @lazy const SPLAT_FFD8 = i16x8.splat(i16(0xd7fe)); /** * Serializes strings into their JSON counterparts using SIMD operations */ export function serializeString_SIMD(src: string): void { let srcStart = changetype(src); const srcInitial = srcStart; const srcSize = changetype(srcStart - TOTAL_OVERHEAD).rtSize; const srcEnd = srcStart + srcSize; do { const srcEnd16Fast = srcEnd - 16; bs.proposeSize(srcSize + 4); const dstStart = bs.offset; let dst = dstStart + 2; while (srcStart < srcEnd16Fast) { const block = load(srcStart); const eq22 = i16x8.eq(block, SPLAT_0022); const eq5C = i16x8.eq(block, SPLAT_005C); const lt20 = i16x8.lt_u(block, SPLAT_0020); const gteD8 = i8x16.gt_u(block, SPLAT_FFD8); const mask = i8x16.bitmask( v128.or(eq22, v128.or(eq5C, v128.or(lt20, gteD8))), ); if (mask != 0) break; store(dst, block); srcStart += 16; dst += 16; } if (srcStart < srcEnd16Fast) break; while (srcStart <= srcEnd - 2) { const code = load(srcStart); if (code > 0x7f || code == BACK_SLASH || code == 34 || code < 32) break; store(dst, code); srcStart += 2; dst += 2; } if (srcStart <= srcEnd - 2) break; store(dstStart, 34); // " store(dst, 34); // " bs.offset = dst + 2; return; } while (false); srcStart = srcInitial; const srcEnd16 = srcEnd - 16; bs.proposeSize(srcSize + 4); store(bs.offset, 34); // " bs.offset += 2; while (srcStart < srcEnd16) { const block = load(srcStart); const eq22 = i16x8.eq(block, SPLAT_0022); const eq5C = i16x8.eq(block, SPLAT_005C); const lt20 = i16x8.lt_u(block, SPLAT_0020); const gteD8 = i8x16.gt_u(block, SPLAT_FFD8); // console.log("\nblock : " + mask_to_string_v128(block)); // console.log("eq22 : " + mask_to_string_v128(eq22) + " -> " + mask_to_string_v128(SPLAT_0022)); // console.log("eq5C : " + mask_to_string_v128(eq5C) + " -> " + mask_to_string_v128(SPLAT_005C)); // console.log("lt20 : " + mask_to_string_v128(lt20) + " -> " + mask_to_string_v128(SPLAT_0020)); // console.log("gteD8 : " + mask_to_string_v128(gteD8) + " -> " + mask_to_string_v128(SPLAT_FFD8)); let mask = i8x16.bitmask( v128.or(eq22, v128.or(eq5C, v128.or(lt20, gteD8))), ); if (mask == 0) { store(bs.offset, block); bs.offset += 16; srcStart += 16; continue; } store(bs.offset, block); do { const laneIdx = ctz(mask); const srcIdx = srcStart + laneIdx; mask &= mask - 1; // Even (0 2 4 6 8 10 12 14) -> Confirmed ASCII Escape // Odd (1 3 5 7 9 11 13 15) -> Possibly a Unicode code unit or surrogate if ((laneIdx & 1) === 0) { const code = load(srcIdx); const escaped = load(SERIALIZE_ESCAPE_TABLE + (code << 2)); if ((escaped & 0xffff) != BACK_SLASH) { bs.growSize(10); const dstIdx = bs.offset + laneIdx; store(dstIdx, U00_MARKER); store(dstIdx, escaped, 8); // memory.copy(dstIdx + 12, srcIdx + 2, 14 - laneIdx); store(dstIdx, load(srcIdx, 2), 12); // unsafe. can overflow here bs.offset += 10; } else { bs.growSize(2); const dstIdx = bs.offset + laneIdx; store(dstIdx, escaped); store(dstIdx, load(srcIdx, 2), 4); // memory.copy(dstIdx + 4, srcIdx + 2, 14 - laneIdx); bs.offset += 2; } continue; } const code = load(srcIdx - 1); // console.log("\nb->" + mask_to_string_v128(block)); // console.log("h->" + mask_to_string_v128(sieve)); // console.log("z->" + mask_to_string_v128(i8x16.ge_u(block,SPLAT_FFD8))); // console.log("m->" + mask.toString(2)); // console.log("l->" + laneIdx.toString()); // console.log("c->" + code.toString(16)); if (code < 0xd800 || code > 0xdfff) continue; if (code <= 0xdbff && srcIdx + 1 <= srcEnd - 2) { const next = load(srcIdx, 1); if (next >= 0xdc00 && next <= 0xdfff) { // paired surrogate mask &= ~(0b11 << (laneIdx + 1)); continue; } } bs.growSize(10); // unpaired high/low surrogate const dstIdx = bs.offset + laneIdx - 1; store(dstIdx, U_MARKER); // \u store(dstIdx, u16_to_hex4_swar(code), 4); // memory.copy(dstIdx + 12, srcIdx + 1, 15 - laneIdx); store(dstIdx, load(srcIdx, 1), 12); bs.offset += 10; } while (mask !== 0); srcStart += 16; bs.offset += 16; } while (srcStart <= srcEnd - 2) { const code = load(srcStart); if (code == 92 || code == 34 || code < 32) { const escaped = load(SERIALIZE_ESCAPE_TABLE + (code << 2)); if ((escaped & 0xffff) != BACK_SLASH) { bs.growSize(10); store(bs.offset, U00_MARKER); store(bs.offset, escaped, 8); bs.offset += 12; } else { bs.growSize(2); store(bs.offset, escaped); bs.offset += 4; } srcStart += 2; continue; } if (code < 0xd800 || code > 0xdfff) { store(bs.offset, code); bs.offset += 2; srcStart += 2; continue; } if (code <= 0xdbff && srcStart + 2 <= srcEnd - 2) { const next = load(srcStart, 2); if (next >= 0xdc00 && next <= 0xdfff) { // valid surrogate pair store(bs.offset, code); store(bs.offset + 2, next); bs.offset += 4; srcStart += 4; continue; } } // unpaired high/low surrogate write_u_escape(code); srcStart += 2; continue; } store(bs.offset, 34); // " bs.offset += 2; } function write_u_escape(code: u16): void { bs.growSize(10); store(bs.offset, U_MARKER); // "\u" store(bs.offset, u16_to_hex4_swar(code), 4); bs.offset += 12; }