nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
io_helpers.hpp
1// toolboxes/builtin/src/io_helpers.hpp
2//
3// Private helpers shared between fileio.cpp (fread/fwrite) and
4// scan.cpp (fscanf/sscanf/textscan). Not a public header — lives
5// in src/ and is only on the PRIVATE include path for toolboxes/builtin.
6
7#pragma once
8
9#include <memory_resource>
10#include <nk/value/value.hpp>
11
12#include <algorithm>
13#include <cctype>
14#include <cmath>
15#include <cstring>
16#include <limits>
17#include <optional>
18#include <string>
19#include <utility>
20
21namespace nk::ops {
22
23// ── SizeSpec: fread / fscanf size-argument parsing ───────────────────
25{
26 enum class Kind { Flat, Matrix };
27 Kind kind;
28 size_t limit;
29 size_t rows;
30 size_t cols;
31 bool matrix() const { return kind == Kind::Matrix; }
32};
33
34inline SizeSpec parseReadSize(const Value &sz, const char *fn)
35{
36 if (sz.isScalar()) {
37 double s = sz.toScalar();
38 if (std::isinf(s))
39 return SizeSpec{SizeSpec::Kind::Flat, SIZE_MAX, 0, 0};
40 if (s < 0 || !std::isfinite(s))
41 throw Error(std::string(fn) + ": size must be Inf or a non-negative integer");
42 return SizeSpec{SizeSpec::Kind::Flat, static_cast<size_t>(s), 0, 0};
43 }
44 if (sz.numel() == 2) {
45 double r = sz(0);
46 double c = sz(1);
47 if (r < 0 || !std::isfinite(r) || std::isinf(r))
48 throw Error(std::string(fn) + ": rows in [m n] must be finite non-negative");
49 size_t rows = static_cast<size_t>(r);
50 size_t cols;
51 size_t limit;
52 if (std::isinf(c)) {
53 cols = SIZE_MAX;
54 limit = SIZE_MAX;
55 } else {
56 if (c < 0 || !std::isfinite(c))
57 throw Error(std::string(fn)
58 + ": cols in [m n] must be finite non-negative or Inf");
59 cols = static_cast<size_t>(c);
60 limit = rows * cols;
61 }
62 return SizeSpec{SizeSpec::Kind::Matrix, limit, rows, cols};
63 }
64 throw Error(std::string(fn) + ": size must be scalar, Inf, or a 2-element vector");
65}
66
67inline Value shapeFreadOutput(const double *values, size_t n,
68 SizeSpec sz, std::pmr::memory_resource *mr)
69{
70 if (!sz.matrix()) {
71 if (n == 0)
72 return Value::matrix(0, 0, ValueType::DOUBLE, mr);
73 Value M = Value::matrix(n, 1, ValueType::DOUBLE, mr);
74 std::memcpy(M.doubleDataMut(), values, n * sizeof(double));
75 return M;
76 }
77 size_t cols_out = (sz.cols == SIZE_MAX)
78 ? (sz.rows == 0 ? 0 : (n + sz.rows - 1) / sz.rows)
79 : sz.cols;
80 if (sz.rows == 0 || cols_out == 0)
81 return Value::matrix(sz.rows, cols_out, ValueType::DOUBLE, mr);
82 Value M = Value::matrix(sz.rows, cols_out, ValueType::DOUBLE, mr);
83 std::memcpy(M.doubleDataMut(), values, n * sizeof(double));
84 return M;
85}
86
87// ── Precision + endian parsing (fread / fwrite) ──────────────────────
88// kind: 0 = unsigned, 1 = signed, 2 = float. byteSize in bytes.
89inline std::optional<std::pair<int, size_t>> parsePrecision(const std::string &raw)
90{
91 std::string p = raw;
92 auto arrow = p.find("=>");
93 if (arrow != std::string::npos) p = p.substr(0, arrow);
94 while (!p.empty() && std::isspace(static_cast<unsigned char>(p.front()))) p.erase(0, 1);
95 while (!p.empty() && std::isspace(static_cast<unsigned char>(p.back()))) p.pop_back();
96
97 if (p == "uint8" || p == "uchar" || p == "char") return std::make_pair(0, size_t{1});
98 if (p == "int8" || p == "schar") return std::make_pair(1, size_t{1});
99 if (p == "uint16") return std::make_pair(0, size_t{2});
100 if (p == "int16") return std::make_pair(1, size_t{2});
101 if (p == "uint32") return std::make_pair(0, size_t{4});
102 if (p == "int32") return std::make_pair(1, size_t{4});
103 if (p == "uint64") return std::make_pair(0, size_t{8});
104 if (p == "int64") return std::make_pair(1, size_t{8});
105 if (p == "single" || p == "float32" || p == "real*4") return std::make_pair(2, size_t{4});
106 if (p == "double" || p == "float64" || p == "real*8") return std::make_pair(2, size_t{8});
107 return std::nullopt;
108}
109
110// Returns true for big-endian, false for little-endian. 'native' → LE
111// because every target we support (x86_64, ARM64, WASM) is LE.
112inline bool parseEndian(const std::string &raw, const char *fn)
113{
114 std::string lo;
115 lo.reserve(raw.size());
116 for (char c : raw) lo += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
117 if (lo == "b" || lo == "ieee-be" || lo == "ieee-be.l64") return true;
118 if (lo == "l" || lo == "ieee-le" || lo == "ieee-le.l64"
119 || lo == "n" || lo == "native")
120 return false;
121 throw Error(std::string(fn) + ": unsupported machine format '" + raw + "'");
122}
123
124inline void byteSwap(char *p, size_t n)
125{
126 for (size_t i = 0, j = n - 1; i < j; ++i, --j) std::swap(p[i], p[j]);
127}
128
129} // namespace nk::ops
Core MATLAB-compatible multidimensional array and scalar value type.
Definition value.hpp:61