Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lang/cpp/include/vortex/array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "vortex/dtype.hpp"
#include "vortex/error.hpp"
#include "vortex/expression.hpp"
#include "vortex/scalar.hpp"
#include "vortex/session.hpp"

#include <vortex.h>
Expand Down Expand Up @@ -211,6 +212,8 @@ class Array {
// Bulk view over Binary values.
BytesView bytes(const Session &session) const;

Scalar scalar_at(const Session &session, size_t index) const;

private:
friend struct detail::Access;
friend class StringView;
Expand Down
81 changes: 77 additions & 4 deletions lang/cpp/include/vortex/scalar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,93 @@ class Scalar {
bool is_null() const;
DataType dtype() const;

/**
* Read scalar's value.
*
* Supported types are bool, primitives, std::string_view, and BinaryView.
*
* std::string_view and BinaryView returned borrow from scalar and stay
* valid while scalar is valid.
*
* Throws if scalar type does not match T or value is Null.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we say this method throws here but it does actually panic right? Shall we validate the dtype and validity and throw so we do what we say?

*/
template <element_type T>
T get() const;

/**
* Read a decimal scalar's unscaled value.
* int8/16/32/64_t are supported.
* Throws if scalar is not a decimal, is Null, or value does not fit in T.
*/
template <primitive_type T>
T get_decimal() const;

private:
friend struct detail::Access;
explicit Scalar(vx_scalar *owned) : handle_(owned) {
explicit Scalar(const vx_scalar *owned) : handle_(owned) {
}
vx_scalar *release() && {
const vx_scalar *release() && {
return handle_.release();
}

struct Deleter {
void operator()(vx_scalar *ptr) const noexcept;
void operator()(const vx_scalar *ptr) const noexcept;
};
std::unique_ptr<vx_scalar, Deleter> handle_;
std::unique_ptr<const vx_scalar, Deleter> handle_;
};

template <element_type T>
T Scalar::get() const {
const vx_scalar *h = handle_.get();
if constexpr (std::is_same_v<T, bool>) {
return vx_scalar_get_bool(h);
} else if constexpr (std::is_same_v<T, std::string_view>) {
vx_view v = vx_scalar_get_utf8(h);
return std::string_view(v.ptr, v.len);
} else if constexpr (std::is_same_v<T, BinaryView>) {
vx_view v = vx_scalar_get_binary(h);
return BinaryView(reinterpret_cast<const std::byte *>(v.ptr), v.len);
} else if constexpr (std::is_same_v<T, uint8_t>) {
return vx_scalar_get_u8(h);
} else if constexpr (std::is_same_v<T, uint16_t>) {
return vx_scalar_get_u16(h);
} else if constexpr (std::is_same_v<T, uint32_t>) {
return vx_scalar_get_u32(h);
} else if constexpr (std::is_same_v<T, uint64_t>) {
return vx_scalar_get_u64(h);
} else if constexpr (std::is_same_v<T, int8_t>) {
return vx_scalar_get_i8(h);
} else if constexpr (std::is_same_v<T, int16_t>) {
return vx_scalar_get_i16(h);
} else if constexpr (std::is_same_v<T, int32_t>) {
return vx_scalar_get_i32(h);
} else if constexpr (std::is_same_v<T, int64_t>) {
return vx_scalar_get_i64(h);
} else if constexpr (std::is_same_v<T, float>) {
return vx_scalar_get_f32(h);
} else if constexpr (std::is_same_v<T, double>) {
return vx_scalar_get_f64(h);
} else {
static_assert(false, "f16 scalar get is not supported");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we not support f16? I mean we do support constructing f16 arrays or scalars, but we can't get a scalar?

}
}

template <primitive_type T>
T Scalar::get_decimal() const {
const vx_scalar *h = handle_.get();
if constexpr (std::is_same_v<T, int8_t>) {
return vx_scalar_get_decimal_i8(h);
} else if constexpr (std::is_same_v<T, int16_t>) {
return vx_scalar_get_decimal_i16(h);
} else if constexpr (std::is_same_v<T, int32_t>) {
return vx_scalar_get_decimal_i32(h);
} else if constexpr (std::is_same_v<T, int64_t>) {
return vx_scalar_get_decimal_i64(h);
} else {
static_assert(false, "unsupported decimal scalar get type");
}
}

namespace detail {
vx_scalar *make_bool(bool value, bool nullable);
vx_scalar *make_primitive(vx_ptype ptype, const void *value, bool nullable);
Expand Down
14 changes: 14 additions & 0 deletions lang/cpp/src/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "vortex/common.hpp"
#include "vortex/dtype.hpp"
#include "vortex/error.hpp"
#include "vortex/scalar.hpp"
#include "vortex/session.hpp"

#include <vortex.h>

Expand Down Expand Up @@ -324,7 +326,19 @@ BytesView Array::bytes(const Session &session) const {
return BytesView(std::move(canonical), std::move(validity), len);
}

Scalar Array::scalar_at(const Session &session, size_t index) const {
vx_error *error = nullptr;
const vx_scalar *scalar = vx_array_get_scalar(Access::c_ptr(session), handle_.get(), index, &error);
throw_on_error(error);
return Access::adopt<Scalar>(scalar);
}

bool PrimitiveView<bool>::value(size_t i) const {
if (i >= size_) {
throw VortexException("index " + std::to_string(i) + " out of bounds for view of size " +
std::to_string(size_),
ErrorCode::OutOfBounds);
}
return vx_array_get_bool(Access::c_ptr(canonical_), i);
}

Expand Down
2 changes: 1 addition & 1 deletion lang/cpp/src/scalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace vortex {
using detail::Access;
using detail::throw_on_error;

void Scalar::Deleter::operator()(vx_scalar *ptr) const noexcept {
void Scalar::Deleter::operator()(const vx_scalar *ptr) const noexcept {
vx_scalar_free(ptr);
}

Expand Down
1 change: 1 addition & 0 deletions lang/cpp/tests/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ TEST_CASE("Operator overloading", "[expr]") {
REQUIRE(bits.value(1));
REQUIRE(bits.value(2));
REQUIRE_FALSE(bits.value(3));
REQUIRE_THROWS_AS(bits.value(data.size()), VortexException);
}

TEST_CASE("Apply error", "[expr]") {
Expand Down
30 changes: 29 additions & 1 deletion lang/cpp/tests/scalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ using namespace std::string_view_literals;

namespace {
using enum vortex::PType;
using scalar::decimal;
using scalar::of;

TEST_CASE("Boolean scalar", "[scalar]") {
Scalar s = scalar::of(true);
Expand All @@ -29,7 +31,7 @@ TEST_CASE("Integer scalars", "[scalar]") {
}

TEST_CASE("Float scalars", "[scalar]") {
REQUIRE(scalar::of(1.5F).dtype().primitive_type() == F32);
REQUIRE(scalar::of(1.5f).dtype().primitive_type() == F32);
REQUIRE(scalar::of(1.5).dtype().primitive_type() == F64);
REQUIRE(scalar::of(float16_t {0x3C00}).dtype().primitive_type() == F16);
}
Expand Down Expand Up @@ -87,6 +89,32 @@ TEST_CASE("Decimal scalars", "[scalar]") {
REQUIRE(d64.dtype().decimal_scale() == 3);
}

TEST_CASE("Primitive getters", "[scalar]") {
REQUIRE(of<uint64_t>(1ULL << 40).get<uint64_t>() == (1ULL << 40));
REQUIRE(of<int32_t>(-7).get<int32_t>() == -7);
REQUIRE(of(2.5).get<double>() == 2.5);
REQUIRE(of(true).get<bool>());
}

TEST_CASE("String getters", "[scalar]") {
Scalar s = of("hello"sv);
REQUIRE(s.get<std::string_view>() == "hello"sv);

const std::byte bytes[] = {std::byte {1}, std::byte {2}, std::byte {0}, std::byte {4}};
Scalar b = of(std::span<const std::byte> {bytes});
BinaryView view = b.get<BinaryView>();
REQUIRE(view.size() == 4);
REQUIRE(view[0] == std::byte {1});
REQUIRE(view[3] == std::byte {4});
}

TEST_CASE("Decimal getters", "[scalar]") {
REQUIRE(decimal<int8_t>(56, 5, 2).get_decimal<int8_t>() == 56);
REQUIRE(decimal<int16_t>(1234, 5, 2).get_decimal<int16_t>() == 1234);
REQUIRE(decimal<int32_t>(5678, 6, 2).get_decimal<int32_t>() == 5678);
REQUIRE(decimal<int64_t>(99999, 12, 3).get_decimal<int64_t>() == 99999);
}

TEST_CASE("Copy scalar", "[scalar]") {
Scalar a = scalar::of<int64_t>(42);
Scalar b = a;
Expand Down
Loading
Loading