#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace DB { /** Функции кодирования: * * IPv4NumToString(num) - См. ниже. * IPv4StringToNum(string) - Преобразуют, например, '192.168.0.1' в 3232235521 и наоборот. * * hex(x) - Возвращает hex; буквы заглавные; префиксов 0x или суффиксов h нет. * Для чисел возвращает строку переменной длины - hex в "человеческом" (big endian) формате, с вырезанием старших нулей, но только по целым байтам. Для дат и дат-с-временем - как для чисел. * Например, hex(257) = '0101'. * unhex(string) - Возвращает строку, hex от которой равен string с точностью до регистра и отбрасывания одного ведущего нуля. * Если такой строки не существует, оставляет за собой право вернуть любой мусор. */ /// Включая нулевой символ в конце. #define MAX_UINT_HEX_LENGTH 20 class FunctionIPv4NumToString : public IFunction { public: /// Получить имя функции. String getName() const { return "IPv4NumToString"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (!dynamic_cast(&*arguments[0])) throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName() + ", expected UInt32", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return new DataTypeString; } static void formatIP(UInt32 ip, char *& out) { char * begin = out; /// Запишем все задом наперед. for (size_t offset = 0; offset <= 24; offset += 8) { if (offset > 0) *(out++) = '.'; /// Достаем очередной байт. UInt32 value = (ip >> offset) & static_cast(255); /// Быстрее, чем sprintf. if (value == 0) { *(out++) = '0'; } else { while (value > 0) { *(out++) = '0' + value % 10; value /= 10; } } } /// И развернем. std::reverse(begin, out); *(out++) = '\0'; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { const ColumnPtr column = block.getByPosition(arguments[0]).column; if (const ColumnVector * col = dynamic_cast *>(&*column)) { const ColumnVector::Container_t & vec_in = col->getData(); ColumnString * col_res = new ColumnString; block.getByPosition(result).column = col_res; ColumnUInt8::Container_t & vec_res = dynamic_cast(col_res->getData()).getData(); ColumnString::Offsets_t & offsets_res = col_res->getOffsets(); vec_res.resize(vec_in.size() * 16); /// самое длинное значение: 255.255.255.255\0 offsets_res.resize(vec_in.size()); char * begin = reinterpret_cast(&vec_res[0]); char * pos = begin; for (size_t i = 0; i < vec_in.size(); ++i) { formatIP(vec_in[i], pos); offsets_res[i] = pos - begin; } vec_res.resize(pos - begin); } else if (const ColumnConst * col = dynamic_cast *>(&*column)) { char buf[16]; char * pos = buf; formatIP(col->getData(), pos); ColumnConstString * col_res = new ColumnConstString(col->size(), buf); block.getByPosition(result).column = col_res; } else throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; class FunctionIPv4StringToNum : public IFunction { public: /// Получить имя функции. String getName() const { return "IPv4StringToNum"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (!dynamic_cast(&*arguments[0])) throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return new DataTypeUInt32; } static inline bool isDigit(char c) { return c >= '0' && c <= '9'; } static UInt32 parseIPv4(const char * pos) { UInt32 res = 0; for (int offset = 24; offset >= 0; offset -= 8) { UInt32 value = 0; size_t len = 0; while (isDigit(*pos) && len <= 3) { value = value * 10 + (*pos - '0'); ++len; ++pos; } if (len == 0 || value > 255 || (offset > 0 && *pos != '.')) return 0; res |= value << offset; ++pos; } if (*(pos - 1) != '\0') return 0; return res; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { const ColumnPtr column = block.getByPosition(arguments[0]).column; if (const ColumnString * col = dynamic_cast(&*column)) { ColumnVector * col_res = new ColumnVector; block.getByPosition(result).column = col_res; ColumnVector::Container_t & vec_res = col_res->getData(); vec_res.resize(col->size()); const ColumnString::DataVector_t & vec_src = col->getDataVector(); const ColumnString::Offsets_t & offsets_src = col->getOffsets(); size_t prev_offset = 0; for (size_t i = 0; i < vec_res.size(); ++i) { vec_res[i] = parseIPv4(reinterpret_cast(&vec_src[prev_offset])); prev_offset = offsets_src[i]; } } else if (const ColumnConstString * col = dynamic_cast(&*column)) { ColumnConst * col_res = new ColumnConst(col->size(), parseIPv4(col->getData().c_str())); block.getByPosition(result).column = col_res; } else throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; class FunctionHex : public IFunction { public: /// Получить имя функции. String getName() const { return "hex"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (!dynamic_cast(&*arguments[0]) && !dynamic_cast(&*arguments[0]) && !dynamic_cast(&*arguments[0]) && !dynamic_cast(&*arguments[0]) && !dynamic_cast(&*arguments[0]) && !dynamic_cast(&*arguments[0]) && !dynamic_cast(&*arguments[0])) throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return new DataTypeString; } template void executeOneUInt(T x, char *& out) { const char digit[17] = "0123456789ABCDEF"; bool was_nonzero = false; for (int offset = (sizeof(T) - 1) * 8; offset >= 0; offset -= 8) { UInt8 byte = static_cast((x >> offset) & 255); /// Ведущие нули. if (byte == 0 && !was_nonzero && offset) continue; was_nonzero = true; *(out++) = digit[byte >> 4]; *(out++) = digit[byte & 15]; } *(out++) = '\0'; } template bool tryExecuteUInt(const IColumn * col, ColumnPtr & col_res) { const ColumnVector * col_vec = dynamic_cast *>(col); const ColumnConst * col_const = dynamic_cast *>(col); if (col_vec) { ColumnString * col_str = new ColumnString; col_res = col_str; ColumnString::DataVector_t & out_vec = col_str->getDataVector(); ColumnString::Offsets_t & out_offsets = col_str->getOffsets(); const typename ColumnVector::Container_t & in_vec = col_vec->getData(); size_t size = in_vec.size(); out_offsets.resize(size); out_vec.resize(size * 3 + MAX_UINT_HEX_LENGTH); size_t pos = 0; for (size_t i = 0; i < size; ++i) { /// Ручной экспоненциальный рост, чтобы не полагаться на линейное амортизированное время работы resize (его никто не гарантирует). if (pos + MAX_UINT_HEX_LENGTH < out_vec.size()) out_vec.resize(out_vec.size() * 2 + MAX_UINT_HEX_LENGTH); char * begin = reinterpret_cast(&out_vec[pos]); char * end = begin; executeOneUInt(in_vec[i], end); pos += end - begin; out_offsets[i] = pos; } out_vec.resize(pos); return true; } else if(col_const) { char buf[MAX_UINT_HEX_LENGTH]; char * pos = buf; executeOneUInt(col_const->getData(), pos); col_res = new ColumnConstString(col_const->size(), buf); return true; } else { return false; } } void executeOneString(const UInt8 * pos, const UInt8 * end, char *& out) { const char digit[17] = "0123456789ABCDEF"; while (pos < end) { UInt8 byte = *(pos++); *(out++) = digit[byte >> 4]; *(out++) = digit[byte & 15]; } *(out++) = '\0'; } bool tryExecuteString(const IColumn * col, ColumnPtr & col_res) { const ColumnString * col_str_in = dynamic_cast(col); const ColumnConstString * col_const_in = dynamic_cast(col); if (col_str_in) { ColumnString * col_str = new ColumnString; col_res = col_str; ColumnString::DataVector_t & out_vec = col_str->getDataVector(); ColumnString::Offsets_t & out_offsets = col_str->getOffsets(); const ColumnString::DataVector_t & in_vec = col_str_in->getDataVector(); const ColumnString::Offsets_t & in_offsets = col_str_in->getOffsets(); size_t size = in_offsets.size(); out_offsets.resize(size); out_vec.resize(in_vec.size() * 2 - size); char * begin = reinterpret_cast(&out_vec[0]); char * pos = begin; size_t prev_offset = 0; for (size_t i = 0; i < size; ++i) { size_t new_offset = in_offsets[i]; executeOneString(&in_vec[prev_offset], &in_vec[new_offset - 1], pos); out_offsets[i] = pos - begin; prev_offset = new_offset; } if (out_offsets.back() != out_vec.size()) throw Exception("Column size mismatch (internal logical error)", ErrorCodes::LOGICAL_ERROR); return true; } else if(col_const_in) { const std::string & src = col_const_in->getData(); std::string res(src.size() * 2, '\0'); char * pos = &res[0]; const UInt8 * src_ptr = reinterpret_cast(src.c_str()); /// Запишем ноль в res[res.size()]. Начиная с C++11, это корректно. executeOneString(src_ptr, src_ptr + src.size(), pos); col_res = new ColumnConstString(col_const_in->size(), res); return true; } else { return false; } } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { const IColumn * column = &*block.getByPosition(arguments[0]).column; ColumnPtr & res_column = block.getByPosition(result).column; if (tryExecuteUInt(column, res_column) || tryExecuteUInt(column, res_column) || tryExecuteUInt(column, res_column) || tryExecuteUInt(column, res_column) || tryExecuteString(column, res_column)) return; throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } }; class FunctionUnhex : public IFunction { public: /// Получить имя функции. String getName() const { return "unhex"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { if (arguments.size() != 1) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + Poco::NumberFormatter::format(arguments.size()) + ", should be 1.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (!dynamic_cast(&*arguments[0])) throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return new DataTypeString; } UInt8 undigitUnsafe(char c) { if (c <= '9') return c - '0'; if (c <= 'Z') return c - ('A' - 10); return c - ('a' - 10); } void unhexOne(const char * pos, const char * end, char *& out) { if ((end - pos) & 1) { *(out++) = undigitUnsafe(*(pos++)); } while (pos < end) { UInt8 major = undigitUnsafe(*(pos++)); UInt8 minor = undigitUnsafe(*(pos++)); *(out++) = (major << 4) | minor; } *(out++) = '\0'; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { const ColumnPtr * column = &*block.getByPosition(arguments[0]).column; if (const ColumnString * col = dynamic_cast(column)) { ColumnString * col_res = new ColumnString; block.getByPosition(result).column = col_res; ColumnString::DataVector_t & out_vec = col_res->getDataVector(); ColumnString::Offsets_t & out_offsets = col_res->getOffsets(); const ColumnString::DataVector_t & in_vec = col->getDataVector(); const ColumnString::Offsets_t & in_offsets = col->getOffsets(); size_t size = in_offsets.size(); out_offsets.resize(size); out_vec.resize(in_vec.size() / 2 + size); char * begin = reinterpret_cast(&out_vec[0]); char * pos = begin; size_t prev_offset = 0; for (size_t i = 0; i < size; ++i) { size_t new_offset = in_offsets[i]; unhexOne(reinterpret_cast(&in_vec[prev_offset]), reinterpret_cast(&in_vec[new_offset - 1]), pos); out_offsets[i] = pos - begin; prev_offset = new_offset; } out_vec.resize(pos - begin); } else if(const ColumnConstString * col = dynamic_cast(column)) { const std::string & src = col->getData(); std::string res(src.size(), '\0'); char * pos = &res[0]; unhexOne(src.c_str(), src.c_str() + src.size(), pos); res = res.substr(0, pos - src.c_str()); block.getByPosition(result).column = new ColumnConstString(col->size(), res); } else { throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } } }; }