Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Milestone] v1 #1

Open
32 tasks
jcbhmr opened this issue May 31, 2023 · 3 comments
Open
32 tasks

[Milestone] v1 #1

jcbhmr opened this issue May 31, 2023 · 3 comments
Milestone

Comments

@jcbhmr jcbhmr added this to the v1 milestone May 31, 2023
@jcbhmr
Copy link
Member Author

jcbhmr commented May 31, 2023

Will need too delve into C++ code to figure out how they do the serialization stuff. 😨

Old code from my Gist which attempted to 1:1 convert some C++ V8 serialization logic
enum SerializationTag {
  version = "\xFF",
  padding = "\0",
  verifyObjectCount = "?"
  // ...
}

enum ArrayBufferViewTag {
  int8Array = "b",
  uint32Array = "B",
  // ...
}

enum ErrorTag {
  evalErrorPrototype = "E",
  rangeError = "R",
  // ...
}

class ValueSerializer {
  #buffer = new ArrayBuffer(0, { maxByteLength: 100 * 1024 });
  #treatArrayBufferViewsAsHostObjects: boolean;

  writeHeader() {
    this.writeTag(SerializationTag.version);
    this.writeVarint(15);
  }

  set treatArrayBufferViewsAsHostObjects(mode: boolean) {
    this.#treatArrayBufferViewsAsHostObjects = mode;
  }

  writeTag(tag: string) {
    const rawTag = tag.charCodeAt(0);
    this.writeRawBytes(Uint8Array.of(rawTag));
  }

  writeVarint(value: number) {
    // Writes an unsigned integer as a base-128 varint.
    // The number is written, 7 bits at a time, from the least significant to the
    // most significant 7 bits. Each byte, except the last, has the MSB set.
    // See also https://developers.google.com/protocol-buffers/docs/encoding
    
    const stackBuffer = new Uint8Array(Number_sizeof(value) * 8 / 7 + 1);
    let nextByte = 0
    do {
      stackBuffer[nextByte] = (value & 0x7f) | 0x80;
      nextByte++;
      value >>= 7;
    } while(value);
    stackBuffer[nextByte - 1] &= 0x7f;
    this.writeRawBytes(stackBuffer.subarray(0, nextByte));
  }

  writeZigZag(value: number) {
    // Writes a signed integer as a varint using ZigZag encoding (i.e. 0 is
    // encoded as 0, -1 as 1, 1 as 2, -2 as 3, and so on).
    // See also https://developers.google.com/protocol-buffers/docs/encoding
    // Note that this implementation relies on the right shift being arithmetic.
    
    this.writeVarint((value << 1)  ^ (value >> (8 * Number_sizeof(value) - 1)));
  }

  writeDouble(value: number) {
    this.writeRawBytes(Float64Array.of(value));
  }

  writeOneByteString(value: string) {
    this.writeVarint(value.length);
    this.writeRawBytes(Uint8Array.from(value, c => c.charCodeAt(0)));
  }

  writeTwoByteString(value: string) {
    this.writeVarint(value.length);
    this.writeRawBytes(Uint16Array.from(value, c => c.charCodeAt(0)));
  }

  writeBigIntContents(value: bigint) {
    const bitField = BigInt_getBitFieldForSerialization(value);
    const byteLength = BigInt_digitsByteLengthForSerialization(bitField);
    this.writeVarint(bitField);
    const bytes = this.reserveRawBytes(byteLength);
    BigInt_serializeDigits(value, bytes);
  }

  writeRawBytes(value: Uint8Array) {
    if (length === 0) {
      return
    }

    const bytes = this.reserveRawBytes(value.length);
    bytes.set(value);
  }

  reserveRawBytes(length: number): Uint8Array {
    this.#buffer.resize(this.#buffer.byteLength + length);
    return new Uint8Array(this.#buffer, this.#buffer.byteLength - length, length);
  }

  writeByte(value: number) {
    const bytes = this.reserveRawBytes(1);  
    bytes[0] = value;
  }

  writeUint32(value: number) {
    this.writeVarint(value);
  }

  writeUint64(value: number) {
    this.writeVarint(value);
  }

  release() {
    const buffer = this.#buffer;
    this.#buffer = new ArrayBuffer(0, { maxByteLength: 100 * 1024 });
  }

  writeObject(value: object) {
    if (isSMI(value)) {
      this.writeSMI(value);
      return
    }

    switch(typeof value) {
      case 
        this.writeOddball(value);
        return
      case "number":
        this.writeHeapNumber(value);
        return
      case "bigint":
        this.writeBigInt(value);
        return
      case ""
  }
}

function v8_serialize(value: any): Uint8Array {

}

@jcbhmr
Copy link
Member Author

jcbhmr commented May 31, 2023

So here's the v8.js file for the node:v8 module: https://github.com/nodejs/node/blob/v20.2.0/lib/v8.js

This is where the C++ Serializer and Deserializer classes seem to be defined: https://github.com/nodejs/node/blob/v20.2.0/src/node_serdes.cc

Then there's ANOTHER layer since those Node.js C++ classes delegate a lot of magic to this:

using v8::ValueDeserializer;
using v8::ValueSerializer;

Maybe<bool> SerializerContext::WriteHostObject(Isolate* isolate,
                                               Local<Object> input) {
  MaybeLocal<Value> ret;
  Local<Value> args[1] = { input };

  Local<Value> write_host_object =
      object()->Get(env()->context(),
                    env()->write_host_object_string()).ToLocalChecked();

  if (!write_host_object->IsFunction()) {
    return ValueSerializer::Delegate::WriteHostObject(isolate, input);
  }

  ret = write_host_object.As<Function>()->Call(env()->context(),
                                               object(),
                                               arraysize(args),
                                               args);

  if (ret.IsEmpty())
    return Nothing<bool>();

  return Just(true);
}

@jcbhmr
Copy link
Member Author

jcbhmr commented May 31, 2023

Then that value serializer is defined in V8! https://github.com/v8/v8/blob/main/src/objects/value-serializer.cc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant