//--------------------------------------------------------------------------------------------------------------------------------- // File: Utility.cpp // Contents: Utility functions used in Microsoft Driver for Node.js for SQL Server // // Copyright Microsoft Corporation and contributors // // Licensed 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 "stdafx.h" #include #include #include #include #include namespace mssql { using namespace v8; string swcvec2str(vector &v, const size_t l) { vector c_str; c_str.reserve(l + 1); c_str.resize(l + 1); constexpr auto c = static_cast(sizeof(SQLWCHAR)); const auto *ptr = reinterpret_cast(v.data()); for (size_t i = 0, j = 0; i < l * c; i += c, j++) { c_str[j] = ptr[i]; } if (l > 0) c_str.resize(l - 1); string s(c_str.data()); return s; } shared_ptr> js2u16(Local str) { const auto str_len = str->Length(); auto query_string = make_shared>(); query_string->reserve(str_len); query_string->resize(str_len); Nan::DecodeWrite(reinterpret_cast(query_string->data()), static_cast(str->Length() * 2), str, Nan::UCS2); return query_string; } vector wstr2wcvec(const wstring &s) { const auto cs = w2sqlc(s); return str2wcvec(cs); } vector str2wcvec(const string &cs) { vector ret; ret.resize(cs.size()); ret.reserve(cs.size()); auto wptr = ret.begin(); for (auto ptr = cs.begin(); ptr != cs.end(); ++ptr, ++wptr) { *wptr = *ptr; } return ret; } string w2sqlc(const wstring &s) { std::wstring_convert> converter; auto c_cs = converter.to_bytes(s); return c_cs; } wstring s2ws(const string &s) { using convert_type = codecvt_utf8; wstring_convert converter; const auto c_cs = converter.from_bytes(s); return wstring{c_cs}; } wstring FromV8String(const Local input) { Nan::Utf8String cons(input); const auto *x = *cons; const string cc = x; auto wides = s2ws(cc); return wides; } int char2_int(const char input) { if (input >= '0' && input <= '9') return input - '0'; if (input >= 'A' && input <= 'F') return input - 'A' + 10; if (input >= 'a' && input <= 'f') return input - 'a' + 10; // throw invalid_argument("Invalid input string"); return 0; } // This function assumes src to be a zero terminated sanitized string with // an even number of [0-9a-f] characters, and target to be sufficiently large int hex2_bin(const char *src, char *target) { auto len = 0; while (*src && src[1]) { *target++ = static_cast(char2_int(*src) * 16 + char2_int(src[1])); src += 2; ++len; } return len; } double round(const double val, const int dp) { const auto raised = pow(10, dp); const auto temp = val * raised; auto rounded = floor(temp); if (temp - rounded >= .5) { rounded = ceil(temp); } return rounded / raised; } string hexify(unsigned long long n) { string res; do { res += "0123456789ABCDEF"[n % 16]; n >>= 4; } while (n); return string{res.rbegin(), res.rend()}; } long strtohextoval(const SQL_NUMERIC_STRUCT &numeric) { long value = 0; int i = 1, last = 1; int a = 0, b = 0; for (i = 0; i <= 15; i++) { const int current = (int)numeric.val[i]; a = current % 16; // Obtain LSD b = current / 16; // Obtain MSD value += last * a; last = last * 16; value += last * b; last = last * 16; } return value; } double decode_numeric_struct(const SQL_NUMERIC_STRUCT &numeric) { // Call to convert the little endian mode data into numeric data. const auto myvalue = strtohextoval(numeric); // The returned value in the above code is scaled to the value specified // in the scale field of the numeric structure. For example 25.212 would // be returned as 25212. The scale in this case is 3 hence the integer // value needs to be divided by 1000. auto divisor = 1; if (numeric.scale > 0) { for (auto i = 0; i < numeric.scale; i++) { divisor = divisor * 10; } } int sign = 0; auto final_val = static_cast(myvalue) / static_cast(divisor); if(!numeric.sign) sign = -1; else sign = 1; final_val *= sign; return final_val; } void encode_numeric_struct(const double v, const int precision, int upscale_limit, SQL_NUMERIC_STRUCT & numeric) { auto encode = fabs(v); double intpart; auto scale = 0; char hex[SQL_MAX_NUMERIC_LEN]; if (upscale_limit <= 0) upscale_limit = SQL_MAX_NUMERIC_LEN; auto dmod = modf(encode, &intpart); while (scale < upscale_limit && dmod != 0.0) { ++scale; encode = encode * 10; dmod = modf(encode, &intpart); } const auto ull = static_cast(encode); memset(numeric.val, 0, SQL_MAX_NUMERIC_LEN); memset(hex, 0, SQL_MAX_NUMERIC_LEN); auto ss = hexify(ull); if (ss.size() % 2 == 1) ss = "0" + ss; const auto len = hex2_bin(ss.c_str(), hex); auto j = 0; for (auto i = len - 1; i >= 0; --i) { numeric.val[j++] = hex[i]; } numeric.sign = v >= 0.0 ? 1 : 0; numeric.precision = precision > 0 ? static_cast(precision) : static_cast(log10(encode) + 1); numeric.scale = static_cast(min(upscale_limit, scale)); } nodeTypeFactory::nodeTypeFactory() { isolate = Isolate::GetCurrent(); } Local nodeTypeFactory::new_long(const int64_t i) const { return Nan::New(static_cast(i)); } Local nodeTypeFactory::new_int32(const int32_t i) const { return Nan::New(i); } Local nodeTypeFactory::new_int64(const int64_t i) const { return Nan::New(static_cast(i)); } Local nodeTypeFactory::new_number() const { return Object::New(isolate); } Local nodeTypeFactory::new_string(const char *cstr) const { return Nan::New(cstr).ToLocalChecked(); } Local nodeTypeFactory::new_string(const char *cstr, const int size) const { return Nan::New(cstr, size).ToLocalChecked(); } Local nodeTypeFactory::new_array() const { return Nan::New(); } Local nodeTypeFactory::new_array(const int count) const { return Array::New(isolate, count); } Local nodeTypeFactory::new_buffer(const int size) const { return Nan::NewBuffer(size).ToLocalChecked(); } Local nodeTypeFactory::error(const stringstream &full_error) const { const auto err = Local::Cast(Exception::Error(new_string(full_error.str().c_str()))); return err; } Local nodeTypeFactory::error(const char *full_error) const { const auto err = Local::Cast(Exception::Error(new_string(full_error))); return err; } Local nodeTypeFactory::new_date() const { return Nan::New(0.0).ToLocalChecked(); } Local nodeTypeFactory::new_date(const double milliseconds, const int32_t nanoseconds_delta) const { const auto ns = Nan::New("nanosecondsDelta").ToLocalChecked(); const auto n = Nan::New(nanoseconds_delta / (NANOSECONDS_PER_MS * 1000.0)); // include the properties for items in a DATETIMEOFFSET that are not included in a JS Date object const auto d = Nan::New(milliseconds).ToLocalChecked(); Nan::Set(d, ns, n); return d; } Local nodeTypeFactory::global() const { return isolate->GetCurrentContext()->Global(); } Local nodeTypeFactory::null() const { return Nan::Null(); } Local nodeTypeFactory::undefined() const { return Undefined(isolate); } void nodeTypeFactory::throwError(const char *err) const { isolate->ThrowException(error(err)); } }