summaryrefslogtreecommitdiff
path: root/src/node_i18n.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/node_i18n.cc')
-rw-r--r--src/node_i18n.cc155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/node_i18n.cc b/src/node_i18n.cc
index 3b33744949..2e1aeaa4cb 100644
--- a/src/node_i18n.cc
+++ b/src/node_i18n.cc
@@ -50,6 +50,8 @@
#include "env-inl.h"
#include "util.h"
#include "util-inl.h"
+#include "base-object.h"
+#include "base-object-inl.h"
#include "v8.h"
#include <unicode/utypes.h>
@@ -86,10 +88,12 @@ namespace node {
using v8::Context;
using v8::FunctionCallbackInfo;
+using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::Object;
+using v8::ObjectTemplate;
using v8::String;
using v8::Value;
@@ -123,6 +127,15 @@ struct Converter {
}
}
+ explicit Converter(UConverter* converter,
+ const char* sub = NULL) : conv(converter) {
+ CHECK_NE(conv, nullptr);
+ UErrorCode status = U_ZERO_ERROR;
+ if (sub != NULL) {
+ ucnv_setSubstChars(conv, sub, strlen(sub), &status);
+ }
+ }
+
~Converter() {
ucnv_close(conv);
}
@@ -130,6 +143,143 @@ struct Converter {
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 {}
+
+ 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());
+
+ 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);
+ }
+
+ Local<ObjectTemplate> t = ObjectTemplate::New(env->isolate());
+ t->SetInternalFieldCount(1);
+ Local<Object> obj = t->NewInstance(env->context()).ToLocalChecked();
+ 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
+
+ Converter utf8("utf8");
+ ConverterObject* converter;
+ ASSIGN_OR_RETURN_UNWRAP(&converter, args[0].As<Object>());
+ SPREAD_BUFFER_ARG(args[1], input_obj);
+ 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_obj_length;
+ if (limit > 0)
+ result.AllocateSufficientStorage(limit);
+
+ UBool flush = (flags & CONVERTER_FLAGS_FLUSH) == CONVERTER_FLAGS_FLUSH;
+
+ const char* source = input_obj_data;
+ size_t source_length = input_obj_length;
+
+ if (converter->unicode_ && !converter->ignoreBOM_ && !converter->bomSeen_) {
+ int32_t bomOffset = 0;
+ ucnv_detectUnicodeSignature(source, source_length, &bomOffset, &status);
+ source += bomOffset;
+ source_length -= bomOffset;
+ converter->bomSeen_ = true;
+ }
+
+ UChar* target = *result;
+ ucnv_toUnicode(converter->conv,
+ &target, target + (limit * sizeof(UChar)),
+ &source, source + source_length,
+ NULL, flush, &status);
+
+ if (U_SUCCESS(status)) {
+ if (limit > 0)
+ result.SetLength(target - &result[0]);
+ ret = ToBufferEndian(env, &result);
+ args.GetReturnValue().Set(ret.ToLocalChecked());
+ goto reset;
+ }
+
+ args.GetReturnValue().Set(status);
+
+ reset:
+ if (flush) {
+ // Reset the converter state
+ converter->bomSeen_ = false;
+ ucnv_reset(converter->conv);
+ }
+ }
+
+ protected:
+ ConverterObject(Environment* env,
+ v8::Local<v8::Object> wrap,
+ UConverter* converter,
+ bool ignoreBOM,
+ const char* sub = NULL) :
+ BaseObject(env, wrap),
+ Converter(converter, sub),
+ ignoreBOM_(ignoreBOM) {
+ MakeWeak<ConverterObject>(this);
+
+ 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,
@@ -717,6 +867,11 @@ void Init(Local<Object> target,
// One-shot converters
env->SetMethod(target, "icuErrName", ICUErrorName);
env->SetMethod(target, "transcode", Transcode);
+
+ // ConverterObject
+ env->SetMethod(target, "getConverter", ConverterObject::Create);
+ env->SetMethod(target, "decode", ConverterObject::Decode);
+ env->SetMethod(target, "hasConverter", ConverterObject::Has);
}
} // namespace i18n