diff --git a/TODO.md b/TODO.md index 16ab05f..70c6169 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ ## Bytecode -- [ ] Byte array +- [x] Byte array - Auto-expand - Add/retrive byte/int/float/string - Should not be larger than the byte array itself diff --git a/src/bytecode/bytearray.cc b/src/bytecode/bytearray.cc index c29cc85..9968642 100644 --- a/src/bytecode/bytearray.cc +++ b/src/bytecode/bytearray.cc @@ -1 +1,84 @@ #include "bytearray.hh" + +#include + +namespace tyche { + +void ByteArray::add_byte(uint32_t addr, uint8_t byte) +{ + try { + data_.at(addr) = byte; + } catch (std::out_of_range&) { + data_.resize(addr + 1, 0); + data_.at(addr) = byte; + } +} + +void ByteArray::add_int(uint32_t addr, int32_t value) +{ + uint32_t zz = ((uint32_t)(value << 1)) ^ ((uint32_t)(value >> 31)); + while (zz > 0x7F) { + add_byte(addr++, (zz & 0x7F) | 0x80); + zz >>= 7; + } + add_byte(addr, zz & 0x7F); +} + +void ByteArray::add_float(uint32_t addr, float value) +{ + uint32_t bits; + std::memcpy(&bits, &value, 4); + add_byte(addr, (uint8_t)(bits)); + add_byte(addr+1, (uint8_t)(bits >> 8)); + add_byte(addr+2, (uint8_t)(bits >> 16)); + add_byte(addr+3, (uint8_t)(bits >> 24)); +} + +void ByteArray::add_string(uint32_t addr, std::string const& str) +{ + for (uint8_t c: str) + add_byte(addr++, c); + add_byte(addr, 0); +} + +uint8_t ByteArray::get_byte(uint32_t addr) const +{ + return data_.at(addr); +} + +std::pair ByteArray::get_int(uint32_t addr) const +{ + uint32_t zz = 0; + int shift = 0; + for (size_t i = 0; shift < 35; i++) { + uint8_t byte = get_byte(addr++); + zz |= (uint32_t)(byte & 0x7F) << shift; + if (!(byte & 0x80)) { + int32_t value = (int32_t)((zz >> 1) ^ -(zz & 1)); + return std::make_pair(value, (int)(i + 1)); + } + shift += 7; + } + throw BytecodeParsingError("Error parsing int32 at position " + std::to_string(addr)); +} + +std::pair ByteArray::get_float(uint32_t addr) const +{ + uint32_t bits = (uint32_t) get_byte(addr) + | (uint32_t) get_byte(addr+1) << 8 + | (uint32_t) get_byte(addr+2) << 16 + | (uint32_t) get_byte(addr+3) << 24; + float value; + std::memcpy(&value, &bits, 4); + return { value, 4 }; +} + +std::pair ByteArray::get_string(uint32_t addr) const +{ + std::string data; + while (char c = (char) get_byte(addr++)) + data += c; + return { data, data.size() + 1 }; +} + +} \ No newline at end of file diff --git a/src/bytecode/bytearray.hh b/src/bytecode/bytearray.hh index a9e0778..a7d5b85 100644 --- a/src/bytecode/bytearray.hh +++ b/src/bytecode/bytearray.hh @@ -1,8 +1,36 @@ #ifndef TYCHE_BYTEARRAY_HH #define TYCHE_BYTEARRAY_HH -class ByteArray { +#include +#include +#include +#include +namespace tyche { + +class ByteArray { +public: + void add_byte(uint32_t addr, uint8_t byte); + void add_int(uint32_t addr, int32_t value); + void add_float(uint32_t addr, float value); + void add_string(uint32_t addr, std::string const& str); + + [[nodiscard]] uint8_t get_byte(uint32_t addr) const; + [[nodiscard]] std::pair get_int(uint32_t addr) const; + [[nodiscard]] std::pair get_float(uint32_t addr) const; + [[nodiscard]] std::pair get_string(uint32_t addr) const; + + [[nodiscard]] std::vector const& data() const { return data_; } + +private: + std::vector data_; }; +class BytecodeParsingError : public std::runtime_error { +public: + explicit BytecodeParsingError(std::string const& str) : std::runtime_error(str.c_str()) {} +}; + +} + #endif //TYCHE_BYTEARRAY_HH diff --git a/src/bytecode/tests.cc b/src/bytecode/tests.cc index 8bb1668..7d5ab87 100644 --- a/src/bytecode/tests.cc +++ b/src/bytecode/tests.cc @@ -1,8 +1,41 @@ #include "gtest/gtest.h" -TEST(test, test) +#include +#include + +#include "bytearray.hh" + +using namespace tyche; + +TEST(ByteArray, ByteArray) { - ASSERT_EQ(1, 1); + auto test = [](std::function const& f, std::vector const& expected) { + ByteArray ba; + f(ba); + ASSERT_EQ(ba.data().size(), expected.size()); + ASSERT_EQ(std::memcmp(ba.data().data(), expected.data(), ba.data().size()), 0); + }; + +#define TESTX(a, ...) test([](ByteArray& ba) { a; }, std::vector({ __VA_ARGS__ })); + + TESTX(ba.add_byte(1, 0xab), 0x00, 0xab) + + ByteArray ba; + ba.add_byte(1, 0xab); ASSERT_EQ(ba.get_byte(1), 0xab); + + ba.add_int(1, 12); ASSERT_EQ(ba.get_int(1), std::make_pair(12, 1)); + ba.add_int(1, -12); ASSERT_EQ(ba.get_int(1), std::make_pair(-12, 1)); + ba.add_int(1, 5000); ASSERT_EQ(ba.get_int(1), std::make_pair(5000, 2)); + ba.add_int(1, 5000300); ASSERT_EQ(ba.get_int(1), std::make_pair(5000300, 4)); + ba.add_int(1, -5000300); ASSERT_EQ(ba.get_int(1), std::make_pair(-5000300, 4)); + + ba.add_float(1, 3.14); ASSERT_FLOAT_EQ(ba.get_float(1).first, 3.14); + ba.add_float(1, -3.14); ASSERT_FLOAT_EQ(ba.get_float(1).first, -3.14); + ba.add_float(1, -5000300.1324); ASSERT_FLOAT_EQ(ba.get_float(1).first, -5000300.1324); + + ba.add_string(1, "Hello world!"); ASSERT_EQ(ba.get_string(1), std::make_pair("Hello world!", 13)); + +#undef TESTX } int main(int argc, char** argv)