Packages
rocksdb
2.4.1
3.1.1
3.1.0
3.0.0
2.6.2
2.6.1
retired
2.6.0
retired
2.5.0
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
1.9.0
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.1
1.1.0
1.0.0
0.26.2
0.26.1
0.26.0
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
RocksDB for Erlang
Current section
Files
Jump to
Current section
Files
c_src/posting_list_nif.cc
// Copyright (c) 2018-2026 Benoit Chesneau
//
// This file is provided 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 <string>
#include <unordered_map>
#include <cstdint>
#include <cstring>
#include "erl_nif.h"
#include "atoms.h"
#include "erocksdb.h"
namespace erocksdb {
// Parse posting list binary: <Len:32><Flag:8><Key:Len>...
// Returns map of key -> is_tombstone (last occurrence wins)
static std::unordered_map<std::string, bool> parse_posting_list(
const unsigned char* data, size_t size)
{
std::unordered_map<std::string, bool> result;
const unsigned char* ptr = data;
const unsigned char* end = data + size;
while (ptr + 5 <= end) {
uint32_t len = (static_cast<uint32_t>(ptr[0]) << 24) |
(static_cast<uint32_t>(ptr[1]) << 16) |
(static_cast<uint32_t>(ptr[2]) << 8) |
static_cast<uint32_t>(ptr[3]);
ptr += 4;
bool is_tombstone = (*ptr != 0);
ptr += 1;
if (ptr + len > end) break;
std::string key(reinterpret_cast<const char*>(ptr), len);
ptr += len;
result[key] = is_tombstone; // Last occurrence wins
}
return result;
}
// posting_list_keys(Binary) -> [binary()]
ERL_NIF_TERM PostingListKeys(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin;
if (argc != 1 || !enif_inspect_binary(env, argv[0], &bin)) {
return enif_make_badarg(env);
}
auto entries = parse_posting_list(bin.data, bin.size);
// Build list of active keys
ERL_NIF_TERM list = enif_make_list(env, 0);
for (const auto& [key, is_tombstone] : entries) {
if (!is_tombstone) {
ERL_NIF_TERM key_bin;
unsigned char* buf = enif_make_new_binary(env, key.size(), &key_bin);
if (buf == nullptr) {
return enif_make_badarg(env);
}
memcpy(buf, key.data(), key.size());
list = enif_make_list_cell(env, key_bin, list);
}
}
return list;
}
// posting_list_contains(Binary, Key) -> boolean()
ERL_NIF_TERM PostingListContains(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin, search_key;
if (argc != 2 ||
!enif_inspect_binary(env, argv[0], &bin) ||
!enif_inspect_binary(env, argv[1], &search_key)) {
return enif_make_badarg(env);
}
std::string key_str(reinterpret_cast<const char*>(search_key.data), search_key.size);
auto entries = parse_posting_list(bin.data, bin.size);
auto it = entries.find(key_str);
if (it != entries.end() && !it->second) {
return ATOM_TRUE;
}
return ATOM_FALSE;
}
// posting_list_find(Binary, Key) -> {ok, boolean()} | not_found
ERL_NIF_TERM PostingListFind(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin, search_key;
if (argc != 2 ||
!enif_inspect_binary(env, argv[0], &bin) ||
!enif_inspect_binary(env, argv[1], &search_key)) {
return enif_make_badarg(env);
}
std::string key_str(reinterpret_cast<const char*>(search_key.data), search_key.size);
auto entries = parse_posting_list(bin.data, bin.size);
auto it = entries.find(key_str);
if (it == entries.end()) {
return ATOM_NOT_FOUND;
}
return enif_make_tuple2(env, ATOM_OK,
it->second ? ATOM_TRUE : ATOM_FALSE);
}
// posting_list_count(Binary) -> non_neg_integer()
ERL_NIF_TERM PostingListCount(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin;
if (argc != 1 || !enif_inspect_binary(env, argv[0], &bin)) {
return enif_make_badarg(env);
}
auto entries = parse_posting_list(bin.data, bin.size);
size_t count = 0;
for (const auto& [key, is_tombstone] : entries) {
if (!is_tombstone) count++;
}
return enif_make_uint64(env, count);
}
// posting_list_to_map(Binary) -> #{binary() => active | tombstone}
ERL_NIF_TERM PostingListToMap(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin;
if (argc != 1 || !enif_inspect_binary(env, argv[0], &bin)) {
return enif_make_badarg(env);
}
auto entries = parse_posting_list(bin.data, bin.size);
ERL_NIF_TERM map = enif_make_new_map(env);
for (const auto& [key, is_tombstone] : entries) {
ERL_NIF_TERM key_bin;
unsigned char* buf = enif_make_new_binary(env, key.size(), &key_bin);
if (buf == nullptr) {
return enif_make_badarg(env);
}
memcpy(buf, key.data(), key.size());
ERL_NIF_TERM value = is_tombstone ? ATOM_TOMBSTONE : ATOM_ACTIVE;
enif_make_map_put(env, map, key_bin, value, &map);
}
return map;
}
} // namespace erocksdb