Skip to content

pioner92/react-native-columnar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

react-native-columnar

react-native-columnar

npm license platform

Zero-copy columnar ArrayBuffer transport from JSI C++ to JavaScript.

JSI modules often return datasets as arrays of objects β€” every row becomes a JS object, every value gets boxed, GC pressure grows. react-native-columnar packs all values into one binary buffer and exposes each column as a typed array view over the same memory. No objects, no parsing, no copy.


⚑ Benchmark

10 000 iterations Β· 5 columns Β· iPhone 16 Pro

id (int32) | status (uint8) | isActive (uint8) | createdAt (double) | updatedAt (double)
Rows Array of objects react-native-columnar Speedup
100 ~418.81 ms ~14.96 ms 27Γ—
500 ~2079.81 ms ~22.06 ms 94Γ—
1000 ~4360.11 ms ~35.89 ms 121Γ—
2000 ~9444.47 ms ~45.39 ms 208Γ—

🎯 Best use cases

SQLite result sets Β· Frame processor outputs Β· Sensor streams Β· Analytics events Β· Realtime charts Β· Large JSI payloads


πŸ“¦ Installation

npm install react-native-columnar
# or
yarn add react-native-columnar

iOS β€” headers are picked up automatically via CocoaPods.

Android β€” app project

Autolinking registers the package automatically. Add to android/app/build.gradle:

android {
  buildFeatures { prefab true }
}

Then in CMakeLists.txt:

find_package(react-native-columnar REQUIRED CONFIG)
target_link_libraries(${YOUR_LIBRARY_NAME} react-native-columnar::react-native-columnar)

Android β€” standalone library

Add to package.json:

{ "dependencies": { "react-native-columnar": "*" } }

Then in CMakeLists.txt (NODE_MODULES_DIR is already passed by any JSI library):

include_directories(${NODE_MODULES_DIR}/react-native-columnar/cpp)

In your C++ files:

#include "react-native-columnar.h"

πŸ”§ C++ side

1. Define a schema

#include "react-native-columnar.h"

#define USER_COLUMNS(X)       \
  X(int32_t, id)              \
  X(uint8_t, status)          \
  X(uint8_t, isActive)        \
  X(double,  createdAt)       \
  X(double,  updatedAt)

DECLARE_BINARY_SCHEMA(UserSchema, USER_COLUMNS)

Generates UserSchema with columnCount, byteSize(), and a Columns struct of std::span views.

2. Write and return an ArrayBuffer

using namespace rn_columnar;

jsi::Value getUsers(jsi::Runtime& rt, const jsi::Value*, const jsi::Value* args, size_t) {
  const uint32_t rows = static_cast<uint32_t>(args[0].asNumber());

  ColumnarWriterBuilder<UserSchema> builder(rows);
  auto cols = UserSchema::createColumns(builder);

  for (uint32_t i = 0; i < rows; ++i) {
    cols.id[i]        = dbRow[i].id;
    cols.status[i]    = dbRow[i].status;
    cols.isActive[i]  = dbRow[i].isActive;
    cols.createdAt[i] = dbRow[i].createdAt;
    cols.updatedAt[i] = dbRow[i].updatedAt;
  }

  return builder.toArrayBuffer(rt); // zero-copy move into JSI
}

🟦 JS side

1. Define the schema

Must match column order and types from C++:

import { createBufferReader, ColumnType } from 'react-native-columnar';

const USER_SCHEMA = [
  ColumnType.Int32,    // id
  ColumnType.Uint8,    // status
  ColumnType.Uint8,    // isActive
  ColumnType.Float64,  // createdAt
  ColumnType.Float64,  // updatedAt
] as const;

2. Read the buffer

const buffer: ArrayBuffer = __getUsers();

const [header, columns] = createBufferReader(buffer, USER_SCHEMA);
const [idCol, statusCol, isActiveCol, createdAtCol, updatedAtCol] = columns;
// idCol β€” Int32Array  |  statusCol β€” Uint8Array  |  createdAtCol β€” Float64Array

const id        = idCol[0];
const status    = statusCol[0];
const isActive  = isActiveCol[0];
const createdAt = createdAtCol[0];
const updatedAt = updatedAtCol[0];

All columns are zero-copy typed array views β€” the buffer is never copied.

API

createBufferReader(buffer: ArrayBuffer, schema: readonly ColumnType[])
  // β†’ [header: Int32Array, columns: TypedArray[]]
  // header[0] = row count, header[1] = column count

ColumnType mapping

ColumnType C++ type JS view Bytes Tip
Int8 int8_t Int8Array 1
Uint8 uint8_t Uint8Array 1 bool, flags
Int16 int16_t Int16Array 2
Uint16 uint16_t Uint16Array 2
Int32 int32_t Int32Array 4 id, count, enum
Uint32 uint32_t Uint32Array 4
Float32 float Float32Array 4 screen coords (~7 sig. digits)
Float64 double Float64Array 8 timestamp, price

⚠️ Limitations

Designed for dense numeric data only. Strings, nullable values, nested objects, and variable-length fields are not supported natively β€” encode them as fixed-width columns using ids, offsets, or sentinel values.


πŸ›  Troubleshooting

react-native-columnar.h not found on Android β€” check that prefab true is enabled and CMake links the package correctly.

std::span errors β€” set C++20 on the target that includes the header.

RangeError in JS β€” JS and C++ schemas are out of sync. Check column order and types match exactly (int32_t β†’ Int32, double β†’ Float64).

Values look shifted β€” one wrong type shifts all following columns. Compare schemas line by line.


Contributing

License

MIT

About

πŸš€ High-performance utility for zero-copy data transport from JSI C++ to JavaScript via columnar ArrayBuffer layout

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors