blob: 94e8c0ad785a9ca525b815cc2e275ae848cbcd3d [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "v8-fast-api-calls.h"
#include <nan.h>
using v8::Local;
using v8::String;
using v8::Value;
template <typename T> constexpr T RoundUp(T a, T b) {
return a % b != 0 ? a + b - (a % b) : a;
}
template <typename T, typename U> constexpr T *AlignUp(T *ptr, U alignment) {
return reinterpret_cast<T *>(
RoundUp(reinterpret_cast<uintptr_t>(ptr), alignment));
}
uint32_t writeVarUint32(uint8_t *dst, uint32_t offset, int32_t value) {
if (value >> 7 == 0) {
dst[offset] = (uint8_t)value;
return 1;
}
if (value >> 14 == 0) {
dst[offset++] = (uint8_t)((value & 0x7F) | 0x80);
dst[offset++] = (uint8_t)(value >> 7);
return 2;
}
if (value >> 21 == 0) {
dst[offset++] = (uint8_t)((value & 0x7F) | 0x80);
dst[offset++] = (uint8_t)(value >> 7 | 0x80);
dst[offset++] = (uint8_t)(value >> 14);
return 3;
}
if (value >> 28 == 0) {
dst[offset++] = (uint8_t)((value & 0x7F) | 0x80);
dst[offset++] = (uint8_t)(value >> 7 | 0x80);
dst[offset++] = (uint8_t)(value >> 14 | 0x80);
dst[offset++] = (uint8_t)(value >> 21);
return 4;
}
dst[offset++] = (uint8_t)((value & 0x7F) | 0x80);
dst[offset++] = (uint8_t)(value >> 7 | 0x80);
dst[offset++] = (uint8_t)(value >> 14 | 0x80);
dst[offset++] = (uint8_t)(value >> 21 | 0x80);
dst[offset++] = (uint8_t)(value >> 28);
return 5;
}
enum Encoding { LATIN1, UTF16, UTF8 };
uint32_t writeUCS2(v8::Isolate *isolate, uint8_t *buf, Local<String> str,
int flags) {
uint16_t *const dst = reinterpret_cast<uint16_t *>(buf);
uint16_t *const aligned_dst = AlignUp(dst, sizeof(*dst));
size_t nchars;
if (aligned_dst == dst) {
nchars = str->Write(isolate, dst, 0, -1, flags);
return nchars * sizeof(*dst);
}
nchars = str->Write(isolate, aligned_dst, 0, str->Length() - 1, flags);
memmove(dst, aligned_dst, nchars * sizeof(*dst));
uint16_t last;
str->Write(isolate, &last, nchars, 1, flags);
memcpy(buf + nchars * sizeof(*dst), &last, sizeof(last));
nchars++;
return nchars * sizeof(*dst);
}
static void serializeString(const v8::FunctionCallbackInfo<v8::Value> &args) {
auto isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
v8::Local<v8::Uint8Array> dst = args[0].As<v8::Uint8Array>();
v8::Local<v8::String> str = args[1].As<v8::String>();
uint32_t offset = args[2].As<v8::Number>()->Uint32Value(context).ToChecked();
bool is_one_byte = str->IsOneByte();
uint8_t *dst_data =
reinterpret_cast<uint8_t *>(dst->Buffer()->GetBackingStore()->Data());
if (is_one_byte && str->IsExternalOneByte()) {
offset += writeVarUint32(dst_data, offset,
(str->Length() << 2) | Encoding::LATIN1); // length
const auto src = str->GetExternalOneByteStringResource()->data();
memcpy(dst_data + offset, src, str->Length());
offset += str->Length();
} else {
v8::HandleScope scope(isolate);
int flags = String::HINT_MANY_WRITES_EXPECTED |
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
if (is_one_byte) {
offset +=
writeVarUint32(dst_data, offset,
(str->Length() << 2) | Encoding::LATIN1); // length
offset += str->WriteOneByte(isolate, dst_data + offset, 0, str->Length(),
flags);
} else {
offset += writeVarUint32(dst_data, offset,
((str->Length() * 2) << 2) |
Encoding::UTF16); // length
offset += writeUCS2(isolate, dst_data + offset, str, flags);
}
}
args.GetReturnValue().Set(offset);
}
static uint32_t serializeStringFast(Local<Value> receiver,
const v8::FastApiTypedArray<uint8_t> &dst,
const v8::FastOneByteString &src,
uint32_t offset, uint32_t max_length) {
uint8_t *dst_data;
dst.getStorageIfAligned(&dst_data);
offset += writeVarUint32(dst_data, offset,
(src.length << 2 | Encoding::LATIN1)); // length
memcpy(dst_data + offset, src.data, src.length);
return offset + src.length;
}
v8::CFunction fast_serialize_string(v8::CFunction::Make(serializeStringFast));
v8::Local<v8::FunctionTemplate> FastFunction(v8::Isolate *isolate,
v8::FunctionCallback callback,
const v8::CFunction *c_function) {
return v8::FunctionTemplate::New(
isolate, callback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect,
c_function);
}
void Init(v8::Local<v8::Object> exports) {
v8::Local<v8::Context> context = exports->GetCreationContextChecked();
v8::Isolate *isolate = context->GetIsolate();
exports
->Set(context, Nan::New("serializeString").ToLocalChecked(),
FastFunction(isolate, serializeString, &fast_serialize_string)
->GetFunction(context)
.ToLocalChecked())
.Check();
}
NODE_MODULE(fory_util, Init)