diff options
author | James M Snell <jasnell@gmail.com> | 2020-03-23 01:33:34 +0300 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2020-03-30 19:50:25 +0300 |
commit | accc984ca93a9d002cc61f5960fcfe5902622cb4 (patch) | |
tree | 1ed8ca852b33b3583febb22348661e4a1cf6275d /src/node_i18n.cc | |
parent | fd7e5d40a4239df10f463a5af2e410fde2cee4cd (diff) |
src: refactoring and cleanup of node_i18n
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/32438
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Diffstat (limited to 'src/node_i18n.cc')
-rw-r--r-- | src/node_i18n.cc | 364 |
1 files changed, 176 insertions, 188 deletions
diff --git a/src/node_i18n.cc b/src/node_i18n.cc index 3bf452b6978..169374aa5de 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -87,7 +87,6 @@ namespace node { using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; -using v8::HandleScope; using v8::Int32; using v8::Isolate; using v8::Local; @@ -118,189 +117,6 @@ MaybeLocal<Object> ToBufferEndian(Environment* env, MaybeStackBuffer<T>* buf) { return ret; } -struct Converter { - explicit Converter(const char* name, const char* sub = nullptr) - : conv(nullptr) { - UErrorCode status = U_ZERO_ERROR; - conv = ucnv_open(name, &status); - CHECK(U_SUCCESS(status)); - if (sub != nullptr) { - ucnv_setSubstChars(conv, sub, strlen(sub), &status); - } - } - - explicit Converter(UConverter* converter, - const char* sub = nullptr) : conv(converter) { - CHECK_NOT_NULL(conv); - UErrorCode status = U_ZERO_ERROR; - if (sub != nullptr) { - ucnv_setSubstChars(conv, sub, strlen(sub), &status); - } - } - - ~Converter() { - ucnv_close(conv); - } - - UConverter* conv; -}; - -class ConverterObject : public BaseObject, Converter { - public: - enum ConverterFlags { - CONVERTER_FLAGS_FLUSH = 0x1, - CONVERTER_FLAGS_FATAL = 0x2, - CONVERTER_FLAGS_IGNORE_BOM = 0x4 - }; - - ~ConverterObject() override = default; - - static void Has(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - HandleScope scope(env->isolate()); - - CHECK_GE(args.Length(), 1); - Utf8Value label(env->isolate(), args[0]); - - UErrorCode status = U_ZERO_ERROR; - UConverter* conv = ucnv_open(*label, &status); - args.GetReturnValue().Set(!!U_SUCCESS(status)); - ucnv_close(conv); - } - - static void Create(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - HandleScope scope(env->isolate()); - - Local<ObjectTemplate> t = env->i18n_converter_template(); - Local<Object> obj; - if (!t->NewInstance(env->context()).ToLocal(&obj)) return; - - CHECK_GE(args.Length(), 2); - Utf8Value label(env->isolate(), args[0]); - int flags = args[1]->Uint32Value(env->context()).ToChecked(); - bool fatal = - (flags & CONVERTER_FLAGS_FATAL) == CONVERTER_FLAGS_FATAL; - bool ignoreBOM = - (flags & CONVERTER_FLAGS_IGNORE_BOM) == CONVERTER_FLAGS_IGNORE_BOM; - - UErrorCode status = U_ZERO_ERROR; - UConverter* conv = ucnv_open(*label, &status); - if (U_FAILURE(status)) - return; - - if (fatal) { - status = U_ZERO_ERROR; - ucnv_setToUCallBack(conv, UCNV_TO_U_CALLBACK_STOP, - nullptr, nullptr, nullptr, &status); - } - - new ConverterObject(env, obj, conv, ignoreBOM); - args.GetReturnValue().Set(obj); - } - - static void Decode(const FunctionCallbackInfo<Value>& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK_GE(args.Length(), 3); // Converter, Buffer, Flags - - ConverterObject* converter; - ASSIGN_OR_RETURN_UNWRAP(&converter, args[0].As<Object>()); - ArrayBufferViewContents<char> input(args[1]); - int flags = args[2]->Uint32Value(env->context()).ToChecked(); - - UErrorCode status = U_ZERO_ERROR; - MaybeStackBuffer<UChar> result; - MaybeLocal<Object> ret; - size_t limit = ucnv_getMinCharSize(converter->conv) * input.length(); - if (limit > 0) - result.AllocateSufficientStorage(limit); - - UBool flush = (flags & CONVERTER_FLAGS_FLUSH) == CONVERTER_FLAGS_FLUSH; - auto cleanup = OnScopeLeave([&]() { - if (flush) { - // Reset the converter state. - converter->bomSeen_ = false; - ucnv_reset(converter->conv); - } - }); - - const char* source = input.data(); - size_t source_length = input.length(); - - UChar* target = *result; - ucnv_toUnicode(converter->conv, - &target, target + (limit * sizeof(UChar)), - &source, source + source_length, - nullptr, flush, &status); - - if (U_SUCCESS(status)) { - bool omit_initial_bom = false; - if (limit > 0) { - result.SetLength(target - &result[0]); - if (result.length() > 0 && - converter->unicode_ && - !converter->ignoreBOM_ && - !converter->bomSeen_) { - // If the very first result in the stream is a BOM, and we are not - // explicitly told to ignore it, then we mark it for discarding. - if (result[0] == 0xFEFF) { - omit_initial_bom = true; - } - converter->bomSeen_ = true; - } - } - ret = ToBufferEndian(env, &result); - if (omit_initial_bom && !ret.IsEmpty()) { - // Peform `ret = ret.slice(2)`. - CHECK(ret.ToLocalChecked()->IsUint8Array()); - Local<Uint8Array> orig_ret = ret.ToLocalChecked().As<Uint8Array>(); - ret = Buffer::New(env, - orig_ret->Buffer(), - orig_ret->ByteOffset() + 2, - orig_ret->ByteLength() - 2) - .FromMaybe(Local<Uint8Array>()); - } - if (!ret.IsEmpty()) - args.GetReturnValue().Set(ret.ToLocalChecked()); - return; - } - - args.GetReturnValue().Set(status); - } - - SET_NO_MEMORY_INFO() - SET_MEMORY_INFO_NAME(ConverterObject) - SET_SELF_SIZE(ConverterObject) - - protected: - ConverterObject(Environment* env, - Local<Object> wrap, - UConverter* converter, - bool ignoreBOM, - const char* sub = nullptr) : - BaseObject(env, wrap), - Converter(converter, sub), - ignoreBOM_(ignoreBOM) { - MakeWeak(); - - switch (ucnv_getType(converter)) { - case UCNV_UTF8: - case UCNV_UTF16_BigEndian: - case UCNV_UTF16_LittleEndian: - unicode_ = true; - break; - default: - unicode_ = false; - } - } - - private: - bool unicode_ = false; // True if this is a Unicode converter - bool ignoreBOM_ = false; // True if the BOM should be ignored on Unicode - bool bomSeen_ = false; // True if the BOM has been seen -}; - // One-Shot Converters void CopySourceBuffer(MaybeStackBuffer<UChar>* dest, @@ -333,10 +149,10 @@ MaybeLocal<Object> Transcode(Environment* env, MaybeStackBuffer<char> result; Converter to(toEncoding, "?"); Converter from(fromEncoding); - const uint32_t limit = source_length * ucnv_getMaxCharSize(to.conv); + const uint32_t limit = source_length * to.max_char_size(); result.AllocateSufficientStorage(limit); char* target = *result; - ucnv_convertEx(to.conv, from.conv, &target, target + limit, + ucnv_convertEx(to.conv(), from.conv(), &target, target + limit, &source, source + source_length, nullptr, nullptr, nullptr, nullptr, true, true, status); if (U_SUCCESS(*status)) { @@ -357,7 +173,7 @@ MaybeLocal<Object> TranscodeToUcs2(Environment* env, MaybeStackBuffer<UChar> destbuf(source_length); Converter from(fromEncoding); const size_t length_in_chars = source_length * sizeof(UChar); - ucnv_toUChars(from.conv, *destbuf, length_in_chars, + ucnv_toUChars(from.conv(), *destbuf, length_in_chars, source, source_length, status); if (U_SUCCESS(*status)) ret = ToBufferEndian(env, &destbuf); @@ -377,7 +193,7 @@ MaybeLocal<Object> TranscodeFromUcs2(Environment* env, const size_t length_in_chars = source_length / sizeof(UChar); CopySourceBuffer(&sourcebuf, source, source_length, length_in_chars); MaybeStackBuffer<char> destbuf(length_in_chars); - const uint32_t len = ucnv_fromUChars(to.conv, *destbuf, length_in_chars, + const uint32_t len = ucnv_fromUChars(to.conv(), *destbuf, length_in_chars, *sourcebuf, length_in_chars, status); if (U_SUCCESS(*status)) { destbuf.SetLength(len); @@ -528,6 +344,178 @@ void ICUErrorName(const FunctionCallbackInfo<Value>& args) { } // anonymous namespace +Converter::Converter(const char* name, const char* sub) { + UErrorCode status = U_ZERO_ERROR; + UConverter* conv = ucnv_open(name, &status); + CHECK(U_SUCCESS(status)); + conv_.reset(conv); + set_subst_chars(sub); +} + +Converter::Converter(UConverter* converter, const char* sub) + : conv_(converter) { + set_subst_chars(sub); +} + +void Converter::set_subst_chars(const char* sub) { + CHECK(conv_); + UErrorCode status = U_ZERO_ERROR; + if (sub != nullptr) { + ucnv_setSubstChars(conv_.get(), sub, strlen(sub), &status); + CHECK(U_SUCCESS(status)); + } +} + +void Converter::reset() { + ucnv_reset(conv_.get()); +} + +size_t Converter::min_char_size() const { + CHECK(conv_); + return ucnv_getMinCharSize(conv_.get()); +} + +size_t Converter::max_char_size() const { + CHECK(conv_); + return ucnv_getMaxCharSize(conv_.get()); +} + +void ConverterObject::Has(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK_GE(args.Length(), 1); + Utf8Value label(env->isolate(), args[0]); + + UErrorCode status = U_ZERO_ERROR; + ConverterPointer conv(ucnv_open(*label, &status)); + args.GetReturnValue().Set(!!U_SUCCESS(status)); +} + +void ConverterObject::Create(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + + Local<ObjectTemplate> t = env->i18n_converter_template(); + Local<Object> obj; + if (!t->NewInstance(env->context()).ToLocal(&obj)) return; + + CHECK_GE(args.Length(), 2); + Utf8Value label(env->isolate(), args[0]); + int flags = args[1]->Uint32Value(env->context()).ToChecked(); + bool fatal = + (flags & CONVERTER_FLAGS_FATAL) == CONVERTER_FLAGS_FATAL; + + UErrorCode status = U_ZERO_ERROR; + UConverter* conv = ucnv_open(*label, &status); + if (U_FAILURE(status)) + return; + + if (fatal) { + status = U_ZERO_ERROR; + ucnv_setToUCallBack(conv, UCNV_TO_U_CALLBACK_STOP, + nullptr, nullptr, nullptr, &status); + } + + new ConverterObject(env, obj, conv, flags); + args.GetReturnValue().Set(obj); +} + +void ConverterObject::Decode(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK_GE(args.Length(), 3); // Converter, Buffer, Flags + + ConverterObject* converter; + ASSIGN_OR_RETURN_UNWRAP(&converter, args[0].As<Object>()); + ArrayBufferViewContents<char> input(args[1]); + int flags = args[2]->Uint32Value(env->context()).ToChecked(); + + UErrorCode status = U_ZERO_ERROR; + MaybeStackBuffer<UChar> result; + MaybeLocal<Object> ret; + size_t limit = converter->min_char_size() * input.length(); + if (limit > 0) + result.AllocateSufficientStorage(limit); + + UBool flush = (flags & CONVERTER_FLAGS_FLUSH) == CONVERTER_FLAGS_FLUSH; + auto cleanup = OnScopeLeave([&]() { + if (flush) { + // Reset the converter state. + converter->set_bom_seen(false); + converter->reset(); + } + }); + + const char* source = input.data(); + size_t source_length = input.length(); + + UChar* target = *result; + ucnv_toUnicode(converter->conv(), + &target, + target + (limit * sizeof(UChar)), + &source, + source + source_length, + nullptr, + flush, + &status); + + if (U_SUCCESS(status)) { + bool omit_initial_bom = false; + if (limit > 0) { + result.SetLength(target - &result[0]); + if (result.length() > 0 && + converter->unicode() && + !converter->ignore_bom() && + !converter->bom_seen()) { + // If the very first result in the stream is a BOM, and we are not + // explicitly told to ignore it, then we mark it for discarding. + if (result[0] == 0xFEFF) + omit_initial_bom = true; + converter->set_bom_seen(true); + } + } + ret = ToBufferEndian(env, &result); + if (omit_initial_bom && !ret.IsEmpty()) { + // Peform `ret = ret.slice(2)`. + CHECK(ret.ToLocalChecked()->IsUint8Array()); + Local<Uint8Array> orig_ret = ret.ToLocalChecked().As<Uint8Array>(); + ret = Buffer::New(env, + orig_ret->Buffer(), + orig_ret->ByteOffset() + 2, + orig_ret->ByteLength() - 2) + .FromMaybe(Local<Uint8Array>()); + } + if (!ret.IsEmpty()) + args.GetReturnValue().Set(ret.ToLocalChecked()); + return; + } + + args.GetReturnValue().Set(status); +} + +ConverterObject::ConverterObject( + Environment* env, + Local<Object> wrap, + UConverter* converter, + int flags, + const char* sub) + : BaseObject(env, wrap), + Converter(converter, sub), + flags_(flags) { + MakeWeak(); + + switch (ucnv_getType(converter)) { + case UCNV_UTF8: + case UCNV_UTF16_BigEndian: + case UCNV_UTF16_LittleEndian: + flags_ |= CONVERTER_FLAGS_UNICODE; + break; + default: { + // Fall through + } + } +} + + bool InitializeICUDirectory(const std::string& path) { UErrorCode status = U_ZERO_ERROR; if (path.empty()) { |