| /* | |
| * 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 "paimon/common/data/binary_string.h" | |
| #include <algorithm> | |
| #include <cctype> | |
| #include <cstdint> | |
| #include "paimon/common/memory/memory_segment_utils.h" | |
| #include "paimon/memory/bytes.h" | |
| #include "paimon/memory/memory_pool.h" | |
| namespace paimon { | |
| const BinaryString& BinaryString::EmptyUtf8() { | |
| static const BinaryString empty_utf8 = BinaryString(); | |
| return empty_utf8; | |
| } | |
| BinaryString::BinaryString(const MemorySegment& segment, int32_t offset, int32_t size_in_bytes) | |
| : BinarySection(segment, offset, size_in_bytes) {} | |
| BinaryString::BinaryString() { | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(0, GetDefaultPool().get()); | |
| segment_ = MemorySegment::Wrap(bytes); | |
| offset_ = 0; | |
| size_in_bytes_ = bytes->size(); | |
| } | |
| BinaryString BinaryString::FromAddress(const MemorySegment& segment, int32_t offset, | |
| int32_t num_bytes) { | |
| return BinaryString(segment, offset, num_bytes); | |
| } | |
| BinaryString BinaryString::FromString(const std::string& str, MemoryPool* pool) { | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(str, pool); | |
| return FromBytes(bytes); | |
| } | |
| BinaryString BinaryString::FromBytes(const std::shared_ptr<Bytes>& bytes) { | |
| return FromBytes(bytes, 0, bytes->size()); | |
| } | |
| BinaryString BinaryString::FromBytes(const std::shared_ptr<Bytes>& bytes, int32_t offset, | |
| int32_t num_bytes) { | |
| return BinaryString(MemorySegment::Wrap(bytes), offset, num_bytes); | |
| } | |
| BinaryString BinaryString::BlankString(int32_t length, MemoryPool* pool) { | |
| std::shared_ptr<Bytes> spaces = Bytes::AllocateBytes(length, pool); | |
| std::fill(spaces->data(), spaces->data() + length, ' '); | |
| return FromBytes(spaces); | |
| } | |
| std::string BinaryString::ToString() const { | |
| std::string ret(size_in_bytes_, '\0'); | |
| MemorySegmentUtils::CopyToBytes({segment_}, offset_, &ret, 0, size_in_bytes_); | |
| return ret; | |
| } | |
| int32_t BinaryString::NumBytesForFirstByte(char b) { | |
| // Classify the leading byte as unsigned: where the ABI makes plain char unsigned, as the | |
| // AArch64 Linux ABI does, a signed test would classify every byte as single byte ASCII. | |
| const auto first_byte = static_cast<uint8_t>(b); | |
| if (first_byte < 0x80) { | |
| // 1 byte, 7 bits: 0xxxxxxx | |
| return 1; | |
| } else if ((first_byte & 0xe0) == 0xc0 && (first_byte & 0x1e) != 0) { | |
| // 2 bytes, 11 bits: 110xxxxx 10xxxxxx | |
| return 2; | |
| } else if ((first_byte & 0xf0) == 0xe0) { | |
| // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx | |
| return 3; | |
| } else if ((first_byte & 0xf8) == 0xf0) { | |
| // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | |
| return 4; | |
| } else { | |
| // Skip the first byte disallowed in UTF-8 | |
| // Handling errors quietly, same semantics to java String. | |
| return 1; | |
| } | |
| } | |
| int32_t BinaryString::CompareTo(const BinaryString& other) const { | |
| int32_t len = std::min(size_in_bytes_, other.size_in_bytes_); | |
| for (int32_t i = 0; i < len; i++) { | |
| int32_t res = | |
| (segment_.Get(offset_ + i) & 0xFF) - (other.segment_.Get(other.offset_ + i) & 0xFF); | |
| if (res != 0) { | |
| return res; | |
| } | |
| } | |
| return size_in_bytes_ - other.size_in_bytes_; | |
| } | |
| int32_t BinaryString::NumChars() const { | |
| int32_t len = 0; | |
| for (int32_t i = 0; i < size_in_bytes_; i += NumBytesForFirstByte(GetByteOneSegment(i))) { | |
| len++; | |
| } | |
| return len; | |
| } | |
| char BinaryString::ByteAt(int32_t index) const { | |
| return segment_.Get(offset_ + index); | |
| } | |
| BinaryString BinaryString::Copy(MemoryPool* pool) const { | |
| std::shared_ptr<Bytes> copy = | |
| MemorySegmentUtils::CopyToBytes({segment_}, offset_, size_in_bytes_, pool); | |
| return FromBytes(copy); | |
| } | |
| BinaryString BinaryString::Substring(int32_t begin_index, int32_t end_index, | |
| MemoryPool* pool) const { | |
| if (end_index <= begin_index || begin_index >= size_in_bytes_) { | |
| return EmptyUtf8(); | |
| } | |
| int32_t i = 0; | |
| int32_t c = 0; | |
| while (i < size_in_bytes_ && c < begin_index) { | |
| i += NumBytesForFirstByte(segment_.Get(i + offset_)); | |
| c += 1; | |
| } | |
| int32_t j = i; | |
| while (i < size_in_bytes_ && c < end_index) { | |
| i += NumBytesForFirstByte(segment_.Get(i + offset_)); | |
| c += 1; | |
| } | |
| if (i > j) { | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(i - j, pool); | |
| segment_.Get(offset_ + j, bytes.get(), 0, i - j); | |
| return FromBytes(bytes); | |
| } else { | |
| return EmptyUtf8(); | |
| } | |
| } | |
| bool BinaryString::Contains(const BinaryString& s) const { | |
| if (s.size_in_bytes_ == 0) { | |
| return true; | |
| } | |
| int32_t find = MemorySegmentUtils::Find({segment_}, offset_, size_in_bytes_, {s.segment_}, | |
| s.offset_, s.size_in_bytes_); | |
| return find != -1; | |
| } | |
| bool BinaryString::StartsWith(const BinaryString& prefix) const { | |
| return MatchAt(prefix, 0); | |
| } | |
| bool BinaryString::EndsWith(const BinaryString& suffix) const { | |
| return MatchAt(suffix, size_in_bytes_ - suffix.size_in_bytes_); | |
| } | |
| int32_t BinaryString::IndexOf(const BinaryString& str, int32_t from_index) const { | |
| if (str.size_in_bytes_ == 0) { | |
| return 0; | |
| } | |
| // position in byte | |
| int32_t byte_idx = 0; | |
| // position is char | |
| int32_t char_idx = 0; | |
| while (byte_idx < size_in_bytes_ && char_idx < from_index) { | |
| byte_idx += NumBytesForFirstByte(GetByteOneSegment(byte_idx)); | |
| char_idx++; | |
| } | |
| do { | |
| if (byte_idx + str.size_in_bytes_ > size_in_bytes_) { | |
| return -1; | |
| } | |
| if (MemorySegmentUtils::Equals({segment_}, offset_ + byte_idx, {str.segment_}, str.offset_, | |
| str.size_in_bytes_)) { | |
| return char_idx; | |
| } | |
| byte_idx += NumBytesForFirstByte(GetByteOneSegment(byte_idx)); | |
| char_idx++; | |
| } while (byte_idx < size_in_bytes_); | |
| return -1; | |
| } | |
| BinaryString BinaryString::ToUpperCase(MemoryPool* pool) const { | |
| if (size_in_bytes_ == 0) { | |
| return EmptyUtf8(); | |
| } | |
| int32_t size = segment_.Size(); | |
| SegmentAndOffset segment_and_offset = StartSegmentAndOffset(size); | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(size_in_bytes_, pool); | |
| for (int32_t i = 0; i < size_in_bytes_; i++) { | |
| char b = segment_and_offset.Value(); | |
| if (NumBytesForFirstByte(b) != 1) { | |
| // fallback | |
| return CppToUpperCase(pool); | |
| } | |
| // A byte >= 0x80 still reaches here via the invalid-first-byte path, and toupper() | |
| // requires a non-negative argument, which plain char does not guarantee. | |
| int32_t upper = toupper(static_cast<unsigned char>(b)); | |
| if (upper > 127) { | |
| // fallback | |
| return CppToUpperCase(pool); | |
| } | |
| (*bytes)[i] = static_cast<char>(upper); | |
| segment_and_offset.NextByte(size); | |
| } | |
| return FromBytes(bytes); | |
| } | |
| BinaryString BinaryString::CppToUpperCase(MemoryPool* pool) const { | |
| std::string str = ToString(); | |
| std::transform(str.begin(), str.end(), str.begin(), | |
| [](unsigned char c) { return static_cast<char>(::toupper(c)); }); | |
| return FromString(str, pool); | |
| } | |
| BinaryString BinaryString::ToLowerCase(MemoryPool* pool) const { | |
| if (size_in_bytes_ == 0) { | |
| return EmptyUtf8(); | |
| } | |
| int32_t size = segment_.Size(); | |
| SegmentAndOffset segment_and_offset = StartSegmentAndOffset(size); | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(size_in_bytes_, pool); | |
| for (int32_t i = 0; i < size_in_bytes_; i++) { | |
| char b = segment_and_offset.Value(); | |
| if (NumBytesForFirstByte(b) != 1) { | |
| // fallback | |
| return CppToLowerCase(pool); | |
| } | |
| // As in ToUpperCase: a byte >= 0x80 must stay non-negative for tolower(). | |
| int32_t lower = tolower(static_cast<unsigned char>(b)); | |
| if (lower > 127) { | |
| // fallback | |
| return CppToLowerCase(pool); | |
| } | |
| (*bytes)[i] = static_cast<char>(lower); | |
| segment_and_offset.NextByte(size); | |
| } | |
| return FromBytes(bytes); | |
| } | |
| BinaryString BinaryString::CppToLowerCase(MemoryPool* pool) const { | |
| std::string str = ToString(); | |
| std::transform(str.begin(), str.end(), str.begin(), | |
| [](unsigned char c) { return static_cast<char>(::tolower(c)); }); | |
| return FromString(str, pool); | |
| } | |
| char BinaryString::GetByteOneSegment(int32_t i) const { | |
| return segment_.Get(offset_ + i); | |
| } | |
| BinaryString::SegmentAndOffset BinaryString::StartSegmentAndOffset(int32_t seg_size) const { | |
| return BinaryString::SegmentAndOffset(segment_, offset_); | |
| } | |
| BinaryString BinaryString::CopyBinaryString(int32_t start, int32_t end, MemoryPool* pool) const { | |
| int32_t len = end - start + 1; | |
| std::shared_ptr<Bytes> new_bytes = Bytes::AllocateBytes(len, pool); | |
| MemorySegmentUtils::CopyToBytes({segment_}, offset_ + start, new_bytes.get(), 0, len); | |
| return FromBytes(new_bytes); | |
| } | |
| bool BinaryString::MatchAt(const BinaryString& s, int32_t pos) const { | |
| return MatchAtOneSeg(s, pos); | |
| } | |
| bool BinaryString::MatchAtOneSeg(const BinaryString& s, int32_t pos) const { | |
| return s.size_in_bytes_ + pos <= size_in_bytes_ && pos >= 0 && | |
| segment_.EqualTo(s.segment_, offset_ + pos, s.offset_, s.size_in_bytes_); | |
| } | |
| std::string_view BinaryString::GetStringView() const { | |
| return std::string_view(segment_.Data() + offset_, size_in_bytes_); | |
| } | |
| } // namespace paimon |