.
This commit is contained in:
2
TODO.md
2
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
|
||||
|
||||
@@ -1 +1,84 @@
|
||||
#include "bytearray.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
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<int32_t, size_t> 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<float, size_t> 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<std::string, size_t> ByteArray::get_string(uint32_t addr) const
|
||||
{
|
||||
std::string data;
|
||||
while (char c = (char) get_byte(addr++))
|
||||
data += c;
|
||||
return { data, data.size() + 1 };
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,36 @@
|
||||
#ifndef TYCHE_BYTEARRAY_HH
|
||||
#define TYCHE_BYTEARRAY_HH
|
||||
|
||||
class ByteArray {
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<int32_t, size_t> get_int(uint32_t addr) const;
|
||||
[[nodiscard]] std::pair<float, size_t> get_float(uint32_t addr) const;
|
||||
[[nodiscard]] std::pair<std::string, size_t> get_string(uint32_t addr) const;
|
||||
|
||||
[[nodiscard]] std::vector<uint8_t> const& data() const { return data_; }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data_;
|
||||
};
|
||||
|
||||
class BytecodeParsingError : public std::runtime_error {
|
||||
public:
|
||||
explicit BytecodeParsingError(std::string const& str) : std::runtime_error(str.c_str()) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //TYCHE_BYTEARRAY_HH
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(test, test)
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "bytearray.hh"
|
||||
|
||||
using namespace tyche;
|
||||
|
||||
TEST(ByteArray, ByteArray)
|
||||
{
|
||||
ASSERT_EQ(1, 1);
|
||||
auto test = [](std::function<void(ByteArray&)> const& f, std::vector<uint8_t> 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<uint8_t>({ __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)
|
||||
|
||||
Reference in New Issue
Block a user