blob: 971dcec2c09d616cfcb1aae5eb036a9117f3cf85 [file]
// 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.
// Binary plain page encoding V3.
//
// The page consists of:
// Data:
// |binary1|binary2|...|binaryN|
// Lengths (contiguous varuint block):
// |varuint_len1|varuint_len2|...|varuint_lenN|
// Trailer:
// |data_block_size(32-bit fixed)|num_elems(32-bit fixed)|
//
// vs V2 (which interleaves length and data per-entry), V3 lets the pre-decoder
// memcpy the entire data block in a single shot when converting to V1 layout,
// and walk the varint lengths once to fill the offsets array.
//
// V3 stores exactly the same bytes as V1/V2 — only the on-disk layout differs. In
// particular, CHAR values keep their trailing '\0' padding on disk (as written by
// OlapColumnDataConvertorChar); that padding is stripped on read by
// BinaryPlainPageV3PreDecoder<true>, selected for (CHAR, PLAIN_ENCODING_V3), exactly
// mirroring PLAIN_ENCODING_V2.
#pragma once
#include "common/logging.h"
#include "core/column/column_complex.h"
#include "core/column/column_nullable.h"
#include "storage/olap_common.h"
#include "storage/segment/binary_plain_page.h"
#include "storage/segment/options.h"
#include "storage/segment/page_builder.h"
#include "storage/segment/page_decoder.h"
#include "storage/types.h"
#include "util/coding.h"
#include "util/faststring.h"
namespace doris {
namespace segment_v2 {
template <FieldType Type>
class BinaryPlainPageV3Builder : public PageBuilderHelper<BinaryPlainPageV3Builder<Type>> {
public:
using Self = BinaryPlainPageV3Builder<Type>;
friend class PageBuilderHelper<Self>;
Status init() override { return reset(); }
bool is_page_full() override {
bool ret = false;
if (_options.is_dict_page) {
ret = _options.dict_page_size != 0 && _size_estimate > _options.dict_page_size;
} else {
ret = _options.data_page_size != 0 && _size_estimate > _options.data_page_size;
}
return ret;
}
Status add(const uint8_t* vals, size_t* count) override {
DCHECK(!_finished);
DCHECK_GT(*count, 0);
size_t i = 0;
while (!is_page_full() && i < *count) {
const auto* src = reinterpret_cast<const Slice*>(vals);
if constexpr (Type == FieldType::OLAP_FIELD_TYPE_BITMAP) {
if (_options.need_check_bitmap) {
RETURN_IF_ERROR(BitmapTypeCode::validate(*(src->data)));
}
}
// Append the data straight into the contiguous data buffer. V3 stores the same
// bytes as V1/V2 (CHAR keeps its '\0' padding, VARCHAR does not); only the layout
// differs. CHAR padding is stripped on read by BinaryPlainPageV3PreDecoder<true>.
RETURN_IF_CATCH_EXCEPTION(_data_buffer.append(src->data, src->size));
// Encode varuint length into a scratch buffer, then append.
uint8_t length_buffer[5]; // max varint32 size
uint8_t* ptr = encode_varint32(length_buffer, cast_set<uint32_t>(src->size));
size_t length_size = ptr - length_buffer;
RETURN_IF_CATCH_EXCEPTION(_lengths_buffer.append(length_buffer, length_size));
_num_elems++;
_size_estimate += src->size + length_size;
_raw_data_size += src->size;
i++;
vals += sizeof(Slice);
}
*count = i;
return Status::OK();
}
Status finish(OwnedSlice* slice) override {
DCHECK(!_finished);
_finished = true;
RETURN_IF_CATCH_EXCEPTION({
// Layout: |data...|lengths...|data_block_size(u32)|num_elems(u32)|
const uint32_t data_block_size = cast_set<uint32_t>(_data_buffer.size());
// Append lengths after data.
_data_buffer.append(_lengths_buffer.data(), _lengths_buffer.size());
// Trailer: data_block_size, num_elems.
put_fixed32_le(&_data_buffer, data_block_size);
put_fixed32_le(&_data_buffer, _num_elems);
*slice = _data_buffer.build();
});
return Status::OK();
}
Status reset() override {
RETURN_IF_CATCH_EXCEPTION({
_data_buffer.clear();
_lengths_buffer.clear();
_data_buffer.reserve(_options.data_page_size == 0 ? 1024
: std::min(_options.data_page_size,
_options.dict_page_size));
_lengths_buffer.reserve(256);
_num_elems = 0;
// Reserve the trailer (data_block_size + num_elems).
_size_estimate = 2 * sizeof(uint32_t);
_finished = false;
_raw_data_size = 0;
});
return Status::OK();
}
size_t count() const override { return _num_elems; }
uint64_t size() const override { return _size_estimate; }
uint64_t get_raw_data_size() const override { return _raw_data_size; }
private:
BinaryPlainPageV3Builder(const PageBuilderOptions& options)
: _size_estimate(0), _options(options) {}
faststring _data_buffer;
faststring _lengths_buffer;
uint32_t _num_elems = 0;
size_t _size_estimate = 0;
bool _finished = false;
PageBuilderOptions _options;
uint64_t _raw_data_size = 0;
};
// V3 decoder behaves identically to the V1 decoder because the V3 pre-decoder
// converts the on-disk V3 layout to the V1 (offsets-array) layout before the
// page is put into the page cache. The decoder operating on the cached page
// therefore only needs to know how to read the V1 layout.
template <FieldType Type>
class BinaryPlainPageV3Decoder : public BinaryPlainPageDecoder<Type> {
public:
BinaryPlainPageV3Decoder(Slice data) : BinaryPlainPageDecoder<Type>(data) {}
BinaryPlainPageV3Decoder(Slice data, const PageDecoderOptions& options)
: BinaryPlainPageDecoder<Type>(data, options) {}
};
} // namespace segment_v2
} // namespace doris