Compare commits

...

24 Commits

Author SHA1 Message Date
BodgeMaster 4c651d1b6f Merge branch 'master' into cygwin 2023-01-09 21:54:23 +01:00
BodgeMaster e152c72a04 fossvg (client): Create window 2023-01-09 20:43:49 +01:00
BodgeMaster 5d80ca801e scripts/clean: Recreate lib/net 2023-01-09 20:38:13 +01:00
BodgeMaster c245e8a5ca lib/{file,javacompat,net/conversion/position,net/conversion/varint,net/packet}: pragma once 2022-12-30 15:38:33 +01:00
BodgeMaster 10d8cdae22 lib/net/{client,server,packet}: Prepare for networking 2022-12-29 03:31:56 +01:00
BodgeMaster b08a86f0b8 resources: Add network capture of a server list ping 2022-12-29 03:28:18 +01:00
BodgeMaster c6ec0f6850 lib/net/conversion/position: Remove misattributed author
Yeah, I just copied the header from somewhere else...
2022-12-27 17:34:16 +01:00
BodgeMaster 85fc73e015 lib/net/conversion/position.hpp: Add library
Unit tests tbd
2022-12-27 17:28:11 +01:00
BodgeMaster d2861b79ac lib/net/conversion/varint: move lib/varint here 2022-12-27 16:24:59 +01:00
BodgeMaster 9403da4ca0 lib/varint: Fix error handling for out of bounds access 2022-12-27 15:44:05 +01:00
BodgeMaster ad5bf1c41a lib/cli: Remove completely useless TODO 2022-12-20 17:06:22 +01:00
BodgeMaster 7108e71b96 lib/varint: Add toVarN() functions 2022-12-20 04:41:09 +01:00
Shwoomple 26df433dc5 lib/file: Implement cutString function 2022-12-04 09:59:28 +05:30
BodgeMaster 44716a55bb lib/varint: Fix fromVar64 function
The problem was that I let the compiler assume data type widths.
`1 << 63` is a 32 bit integer on x86_64-pc-linux-gnu.
2022-11-26 14:50:02 +01:00
BodgeMaster fe7c763d06 lib/varint: Add VarInt library
Minecraft uses 32-bit and 64-bit VarInt types to cut down on network usage.
This library currently contains read functions for conversion to normal integers.

Something seems to be wrong with the converter for 64-bit varints,
can’t figure out what rn.
2022-11-25 22:19:49 +01:00
BodgeMaster d392e080ca test/nbt_tags: Add tests for constructing invalid lists/compounds 2022-11-25 17:07:35 +01:00
BodgeMaster 60a8ac9788 lib/nbt: Make constructor NBT::Tag::Compound::Compound(name, data) private
The constructor has been made private and replaced with a static wrapper
function to make constructing from invalid data impossible.

If there is more than one end tag or an end tag isn’t at the end,
an error will be returned.
2022-11-25 15:52:24 +01:00
Shwoomple bc2255de6b lib/file: Fix issue #71 - Electric Boogaloo (Out of bounds access)
Add size constraint to make sure string terminates at the correct
length. Cygwin appears to not put null bytes to terminate string when
reading from a file stream.
2022-11-12 11:51:43 +05:30
Shwoomple e627714b44 lib/file: Implement cut function. 2022-11-12 06:36:04 +01:00
Shwoomple 42b7e40f9d lib/file:Fix issue #71 (Out of Bounds access) 2022-11-12 06:36:04 +01:00
Shwoomple 9bda607649 lib/file: Implement cut function. 2022-11-12 10:59:08 +05:30
Shwoomple 76dd30c45a lib/file:Fix issue #71 (Out of Bounds access) 2022-11-12 10:58:27 +05:30
Shwoomple f784948c3e lib/file: fix cutByte filesize bug 2022-10-30 16:10:33 +00:00
Shwoomple d794bce288 lib/file.cpp: Implement cutByte function 2022-10-30 16:10:33 +00:00
25 changed files with 2263 additions and 20 deletions

View File

@ -26,6 +26,7 @@ Build dependencies:
- bash - bash
- a C++ 20 compiler - a C++ 20 compiler
- GLFW with headers
Setup dependencies: Setup dependencies:

View File

@ -27,6 +27,14 @@ Data used to test the NBT library
`level.dat_decompressed`: The same data decompressed `level.dat_decompressed`: The same data decompressed
## network_capture/
Network captures used to get an understanding of the protocol
`ping.pcapng`: WireShark capture of the multipayer screen server ping
`ping_decoded.txt`: Extracted TCP payloads from the network capture annotated with whats happening
## unicode_data/ ## unicode_data/
Files with unicode data Files with unicode data

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -63,7 +63,7 @@ COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/tools/arraydump.cpp -I./include -Lbin/lib -l:file.so -l:cli.so -o bin/tools/arraydump" "$CXX_WITH_FLAGS src/tools/arraydump.cpp -I./include -Lbin/lib -l:file.so -l:cli.so -o bin/tools/arraydump"
"$CXX_WITH_FLAGS src/tools/baseconvert.cpp -I./include -Lbin/lib -l:cli.so -o bin/tools/baseconvert" "$CXX_WITH_FLAGS src/tools/baseconvert.cpp -I./include -Lbin/lib -l:cli.so -o bin/tools/baseconvert"
"$CXX_WITH_FLAGS -pthread src/tools/hexnet.cpp -I./include -Lbin/lib -l:cli.so -l:libsockpp.so -o bin/tools/hexnet" "$CXX_WITH_FLAGS -pthread src/tools/hexnet.cpp -I./include -Lbin/lib -l:cli.so -l:libsockpp.so -o bin/tools/hexnet"
"$CXX_WITH_FLAGS src/fossvg.cpp -I./include -Lbin/lib -l:cli.so -o bin/fossvg" "$CXX_WITH_FLAGS src/fossvg.cpp -I./include -Lbin/lib -l:cli.so -lglfw -o bin/fossvg"
"$CXX_WITH_FLAGS src/fossvgd.cpp -I./include -Lbin/lib -l:cli.so -o bin/fossvgd" "$CXX_WITH_FLAGS src/fossvgd.cpp -I./include -Lbin/lib -l:cli.so -o bin/fossvgd"
) )
for command in ${!COMPILE_COMMANDS[@]}; do for command in ${!COMPILE_COMMANDS[@]}; do

View File

@ -25,6 +25,7 @@ remove .endianness
remove resources/check_endianness remove resources/check_endianness
create_directory ./bin create_directory ./bin
create_directory ./bin/lib create_directory ./bin/lib
create_directory ./bin/lib/net
create_directory ./include create_directory ./include
if uname -s | tr [:upper:] [:lower:] | grep cygwin >/dev/null; then if uname -s | tr [:upper:] [:lower:] | grep cygwin >/dev/null; then

View File

@ -39,6 +39,7 @@ COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/test/nbt_tags.cpp -I./include -Lbin/lib -l:nbt.so -o bin/test/nbt_tags" "$CXX_WITH_FLAGS src/test/nbt_tags.cpp -I./include -Lbin/lib -l:nbt.so -o bin/test/nbt_tags"
"$CXX_WITH_FLAGS src/test/nbt_size_helpers.cpp -I./include -Lbin/lib -l:nbt.so -o bin/test/nbt_size_helpers" "$CXX_WITH_FLAGS src/test/nbt_size_helpers.cpp -I./include -Lbin/lib -l:nbt.so -o bin/test/nbt_size_helpers"
"$CXX_WITH_FLAGS src/test/file.cpp -I./include -Lbin/lib -l:file.so -o bin/test/file" "$CXX_WITH_FLAGS src/test/file.cpp -I./include -Lbin/lib -l:file.so -o bin/test/file"
"$CXX_WITH_FLAGS src/test/varint.cpp -I./include -Lbin/lib -o bin/test/varint"
) )
for command in ${!COMPILE_COMMANDS[@]}; do for command in ${!COMPILE_COMMANDS[@]}; do
echo "${COMPILE_COMMANDS[command]}" echo "${COMPILE_COMMANDS[command]}"

View File

@ -18,6 +18,10 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <cstdint>
#include <string>
#include <GLFW/glfw3.h>
#include "./lib/cli.hpp" #include "./lib/cli.hpp"
@ -25,6 +29,27 @@
#define EXIT_RUNTIME 1 #define EXIT_RUNTIME 1
#define EXIT_USAGE 2 #define EXIT_USAGE 2
//TODO: check the TODO above glfwInit() in void main()
// #### Callbacks ##############################################################
void cursorPositionCallback(GLFWwindow* window, double x, double y) {
}
void keyCallback(GLFWwindow* window, int32_t key, int32_t scancode, int32_t action, int32_t mods) {
}
void textInputCallback(GLFWwindow* window, uint32_t codepoint) {
}
void cursorEnterLeaveCallback(GLFWwindow* window, int32_t entered) {
}
void mouseButtonCallback(GLFWwindow* window, int32_t button, int32_t action, int32_t mods) {
}
void scrollCallback(GLFWwindow* window, double x, double y) {
}
// #### End Callbacks ##########################################################
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::vector<CLI::Flag> flags; std::vector<CLI::Flag> flags;
flags.push_back(CLI::Flag('h', "help", "print help and exit")); flags.push_back(CLI::Flag('h', "help", "print help and exit"));
@ -63,5 +88,50 @@ int main(int argc, char* argv[]) {
return EXIT_USAGE; return EXIT_USAGE;
} }
// TODO: Find a better place for this
// Ideally, the window management and rendering portion of FOSS-VG should
// live in a library so it can be easily reloaded/restarted for things like
// switching from/to fullscreen. For now, I am going to put it here because
// I want to get it going before making it pretty.
{
if (!glfwInit()) {
return EXIT_RUNTIME;
}
//TODO: allow to set startup window size using CLI options
uint32_t windowWidth = 1366;
uint32_t windowHeight = 768;
//TODO: add a version macro
// (for example Git commit hash passed on the compiler command line)
std::string windowTitle = "FOSS-VG";
// Apparently, this also allows to set things like whether the window is full-screen
GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, windowTitle.c_str(), nullptr, nullptr);
if (window == nullptr) {
return EXIT_RUNTIME;
}
// What dis do? It was in a tutorial.
glfwMakeContextCurrent(window);
glfwSetCursorPosCallback(window, cursorPositionCallback);
glfwSetKeyCallback(window, keyCallback);
glfwSetCharCallback(window, textInputCallback);
glfwSetCursorEnterCallback(window, cursorEnterLeaveCallback);
glfwSetMouseButtonCallback(window, mouseButtonCallback);
glfwSetScrollCallback(window, scrollCallback);
while (!glfwWindowShouldClose(window)) {
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -202,7 +202,6 @@ namespace CLI {
} }
ArgumentsParser::~ArgumentsParser() { ArgumentsParser::~ArgumentsParser() {
//TODO: check that this actually runs
for (auto const& [shortName, flag]: this->flagsByShortName) { for (auto const& [shortName, flag]: this->flagsByShortName) {
delete flag; delete flag;
} }

View File

@ -97,6 +97,9 @@ namespace ErrorCodes {
const uint8_t MIXED_TYPES = 12; const uint8_t MIXED_TYPES = 12;
// when too much data is available
const uint8_t OVERFLOW = 13;
const uint8_t UNIMPLEMENTED = 254; const uint8_t UNIMPLEMENTED = 254;
const uint8_t UNKNOWN = 255; const uint8_t UNKNOWN = 255;

View File

@ -65,6 +65,8 @@ void File::open(){
void File::close(){ void File::close(){
this->fileStream.close(); this->fileStream.close();
std::filesystem::path filePath = this->path;
this->size = ErrorOr<uint64_t>(std::filesystem::file_size(filePath));
this->isOpen = false; this->isOpen = false;
} }
@ -187,7 +189,7 @@ ErrorOrVoid File::insertByte(uint8_t byte){
this->fileStream.seekg(0); this->fileStream.seekg(0);
this->write(readData); this->write(readData);
this->cursorPosition++; this->cursorPosition++;
delete buffer; delete[] buffer;
}catch(std::exception& e){ }catch(std::exception& e){
failure = true; failure = true;
} }
@ -209,7 +211,7 @@ ErrorOrVoid File::insert(std::vector<uint8_t> data){
this->fileStream.seekg(0); this->fileStream.seekg(0);
this->write(readData); this->write(readData);
this->cursorPosition += data.size(); this->cursorPosition += data.size();
delete buffer; delete[] buffer;
}catch(std::exception& e){ }catch(std::exception& e){
failure = true; failure = true;
} }
@ -225,15 +227,14 @@ ErrorOrVoid File::insertString(tiny_utf8::string string){
this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value); this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value);
readData = tiny_utf8::string((char *) buffer); readData = tiny_utf8::string((char *) buffer, 0, this->size.value);
readData.insert(readData.begin()+this->cursorPosition, string); readData.insert(readData.begin()+this->cursorPosition, string);
this->fileStream.seekg(0); this->fileStream.seekg(0);
//TODO: fix hack. tinyutf8 appends "_utf-8" when readData is assigned: tiny_utf8::string((char *) buffer); this->writeString(readData);
this->writeString(readData.substr(0, readData.find("_utf-8")));
this->cursorPosition += string.size(); this->cursorPosition += string.size();
delete buffer; delete[] buffer;
}catch(std::exception& e){ }catch(std::exception& e){
failure = true; failure = true;
} }
@ -256,14 +257,64 @@ ErrorOr<uint8_t> File::cutByte(){
std::filesystem::resize_file(this->path, readData.size()); std::filesystem::resize_file(this->path, readData.size());
this->fileStream.seekg(0); this->fileStream.seekg(0);
this->write(readData); this->write(readData);
this->cursorPosition++; //this->cursorPosition++;
delete buffer; delete[] buffer;
}catch(std::exception& e){ }catch(std::exception& e){
failure = true; failure = true;
} }
return failure ? ErrorOr<uint8_t>(true, ErrorCodes::UNKNOWN) : ErrorOr<uint8_t>(byte); return failure ? ErrorOr<uint8_t>(true, ErrorCodes::UNKNOWN) : ErrorOr<uint8_t>(byte);
} }
ErrorOr<std::vector<uint8_t>> File::cut(uint64_t length){
bool failure = false;
std::vector<uint8_t> bytes;
try{
uint8_t* buffer = new uint8_t[this->size.value];
std::vector<uint8_t> readData;
this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value);
readData = std::vector<uint8_t>(buffer, buffer+this->size.value);
bytes = std::vector<uint8_t>(readData.begin() + this->cursorPosition, readData.begin() + (this->cursorPosition + length));
readData.erase(readData.begin() + this->cursorPosition, readData.begin() + (this->cursorPosition + length));
std::filesystem::resize_file(this->path, readData.size());
this->fileStream.seekg(0);
this->write(readData);
//this->cursorPosition += length;
delete[] buffer;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::UNKNOWN) :ErrorOr<std::vector<uint8_t>>(bytes);
}
ErrorOr<tiny_utf8::string> File::cutString(uint64_t length){
bool failure = false;
tiny_utf8::string cutString;
try{
uint8_t* buffer = new uint8_t[this->size.value];
tiny_utf8::string readData;
this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value);
readData = tiny_utf8::string((char *) buffer, 0, this->size.value);
cutString = readData.substr(this->cursorPosition, length);
std::filesystem::resize_file(this->path, readData.size()-cutString.size());
this->fileStream.seekg(0);
this->writeString(readData.substr(this->cursorPosition+length));
//this->cursorPosition += length;
delete[] buffer;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOr<tiny_utf8::string>(true, ErrorCodes::UNKNOWN) : ErrorOr<tiny_utf8::string>(cutString);
}
ErrorOr<File*> File::open(std::string path, char mode, uint64_t startPosition){ ErrorOr<File*> File::open(std::string path, char mode, uint64_t startPosition){
if (!std::filesystem::exists(path) && (mode == 'r' || mode == 'm')) { if (!std::filesystem::exists(path) && (mode == 'r' || mode == 'm')) {
return ErrorOr<File*>(true, ErrorCodes::FILE_NOT_FOUND, nullptr); return ErrorOr<File*>(true, ErrorCodes::FILE_NOT_FOUND, nullptr);

View File

@ -16,6 +16,8 @@
// version 3 along with this program. // version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#pragma once
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <fstream> #include <fstream>

View File

@ -13,6 +13,8 @@
//version 3 along with this program. //version 3 along with this program.
//If not, see https://www.gnu.org/licenses/agpl-3.0.en.html //If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#pragma once
#include <tinyutf8/tinyutf8.h> #include <tinyutf8/tinyutf8.h>
#include <string> #include <string>
#include "error.hpp" #include "error.hpp"

View File

@ -976,6 +976,20 @@ namespace NBT {
this->endPointer = new End(); this->endPointer = new End();
} }
ErrorOr<Compound*> Compound::constructWithData(tiny_utf8::string name, std::vector<Generic*> data) {
if (data.size() > 0) {
for (uint64_t i=0; i<data.size(); i++) {
if (data[i]->getTagType() == TagType::END && i != data.size()-1) {
return ErrorOr<Compound*>(true, ErrorCodes::NOT_ALLOWED, nullptr);
}
}
if (data[data.size()-1]->getTagType() == TagType::END) {
return ErrorOr<Compound*>(new Compound(name, std::vector(data.begin(), data.end()-1)));
}
}
return ErrorOr<Compound*>(new Compound(name, data));
}
Compound::~Compound() { Compound::~Compound() {
for (uint64_t i=0; i<this->tags.size(); i++) { for (uint64_t i=0; i<this->tags.size(); i++) {
delete this->tags.at(i); delete this->tags.at(i);
@ -1342,7 +1356,7 @@ namespace NBT {
returnValue = ErrorOr<std::vector<Tag::Generic*>>(true, nextCompoundData.errorCode); returnValue = ErrorOr<std::vector<Tag::Generic*>>(true, nextCompoundData.errorCode);
goto returnError; goto returnError;
} }
contents.push_back(new Tag::Compound("", nextCompoundData.value)); contents.push_back(reinterpret_cast<Tag::Generic*>(Tag::Compound::constructWithData("", nextCompoundData.value).value));
*processedDataSize += *containedDataSize; *processedDataSize += *containedDataSize;
} }
delete containedDataSize; delete containedDataSize;
@ -1454,7 +1468,7 @@ namespace NBT {
returnValue = ErrorOr<std::vector<Tag::Generic*>>(true, compoundData.errorCode); returnValue = ErrorOr<std::vector<Tag::Generic*>>(true, compoundData.errorCode);
goto returnNow; goto returnNow;
} }
tags.push_back(new Tag::Compound(tagName.value, compoundData.value)); tags.push_back(reinterpret_cast<Tag::Generic*>(Tag::Compound::constructWithData(tagName.value, compoundData.value).value));
*processedTagSize += (uint64_t) nameSize + 3; *processedTagSize += (uint64_t) nameSize + 3;
} }
currentPosition += *processedTagSize; currentPosition += *processedTagSize;

View File

@ -248,10 +248,10 @@ namespace NBT {
std::vector<Generic*> tags; std::vector<Generic*> tags;
// built-in end tag // built-in end tag
End* endPointer; End* endPointer;
Compound(tiny_utf8::string name, std::vector<Generic*> data);
public: public:
Compound(); Compound();
Compound(tiny_utf8::string name); Compound(tiny_utf8::string name);
Compound(tiny_utf8::string name, std::vector<Generic*> data);
~Compound() override; ~Compound() override;
@ -262,6 +262,7 @@ namespace NBT {
ErrorOrVoid appendPointer(Generic* pointer); ErrorOrVoid appendPointer(Generic* pointer);
ErrorOrVoid deleteElement(uint64_t position); ErrorOrVoid deleteElement(uint64_t position);
uint64_t length(); uint64_t length();
static ErrorOr<Compound*> constructWithData(tiny_utf8::string name, std::vector<Generic*> data);
}; };
class Int32Array: public Generic { class Int32Array: public Generic {

16
src/lib/net/client.cpp Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

16
src/lib/net/client.hpp Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

View File

@ -0,0 +1,70 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
// BodgeMaster
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#pragma once
#include <cstdint>
#include <vector>
#include "../../error.hpp"
namespace Position {
struct Location {
// 26 bit integer
int32_t x;
// 12 bit integer
int16_t y;
// 26 bit integer
int32_t z;
}
Location fromPosition(uint64_t position) {
Location location;
location.x = (int32_t) ((0xFFFFFFC000000000 & position) >> 38);
if (location.x >= 0x02000000) {
location.x -= 0x04000000;
}
location.y = (int16_t) ((0x0000000000000FFF & position));
if (location.y >= 0x0800) {
location.y -= 0x1000;
}
location.z = (int32_t) ((0x0000003FFFFFF000 & position) >> 12);
if (location.z >= 0x02000000) {
location.z -= 0x04000000;
}
}
ErrorOr<Location> fromPosition(std::vector data, uint64_t initialPosition=0) {
if (inititalPosition >= data.size()) {
return ErrorOr<Location>(true, ErrorCodes::OUT_OF_BOUNDS);
}
if (initialPosition+7 >= data.size()) {
return ErrorOr<Location>(true, ErrorCodes::OVERRUN);
}
uint64_t deserialized = 0;
for (uint8_t i=0; i<8; i++) {
deserialized += (uint64_t) data[initialPosition+i] << i*8;
}
return ErrorOr<Location>(fromPosition(deserialized));
}
}

View File

@ -0,0 +1,141 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
// BodgeMaster, Shwoomple
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#pragma once
#include <cstdint>
#include <vector>
#include "../../error.hpp"
namespace VarInt {
// up to 5 bytes, least significant byte first, most significant bit
// indicates whether the next byte is still part of the number
ErrorOr<int32_t> fromVar32(std::vector<uint8_t> data, uint64_t initialPosition=0, uint8_t* processedBytes=nullptr) {
if (initialPosition >= data.size()) {
return ErrorOr<int32_t>(true, ErrorCodes::OUT_OF_RANGE);
}
int32_t returnValue = 0;
uint64_t currentPosition = initialPosition;
uint8_t bits = 0;
while (data[currentPosition] & 0b10000000 && currentPosition < initialPosition+4) {
returnValue = returnValue + (((int32_t) 0b01111111 & data[currentPosition]) << bits);
(*processedBytes)++;
bits += 7;
currentPosition++;
// check after increasing so we don't need to check outside the loop
if (currentPosition >= data.size()) {
return ErrorOr<int32_t>(true, ErrorCodes::OVERRUN);
}
}
if (data[currentPosition] & 0b10000000) {
return ErrorOr<int32_t>(true, ErrorCodes::OVERFLOW);
}
returnValue = returnValue + (((int32_t) 0b01111111 & data[currentPosition]) << bits);
(*processedBytes)++;
return ErrorOr<int32_t>(returnValue);
}
// up to 10 bytes, least significant byte first, most significant bit
// indicates whether the next byte is still part of the number
ErrorOr<int64_t> fromVar64(std::vector<uint8_t> data, uint64_t initialPosition=0, uint8_t* processedBytes=nullptr) {
if (initialPosition >= data.size()) {
return ErrorOr<int64_t>(true, ErrorCodes::OUT_OF_RANGE);
}
int64_t returnValue = 0;
uint64_t currentPosition = initialPosition;
uint8_t bits = 0;
while (data[currentPosition] & 0b10000000 && currentPosition < initialPosition+9) {
returnValue = returnValue + (((int64_t) 0b01111111 & data[currentPosition]) << bits);
(*processedBytes)++;
bits += 7;
currentPosition++;
// check after increasing so we don't need to check outside the loop
if (currentPosition >= data.size()) {
return ErrorOr<int64_t>(true, ErrorCodes::OVERRUN);
}
}
if (data[currentPosition] & 0b10000000) {
return ErrorOr<int64_t>(true, ErrorCodes::OVERFLOW);
}
returnValue = returnValue + (((int64_t) 0b01111111 & data[currentPosition]) << bits);
(*processedBytes)++;
return ErrorOr<int64_t>(returnValue);
}
// appends to the vector
void toVar32(int32_t value, std::vector<uint8_t> &data) {
uint8_t nextByte;
uint8_t shift = 0;
// do something else after the 4th shift
while (shift < 4) {
if (shift>0 && value==0) {
return;
}
nextByte = value & 0b01111111;
value = (value >> 7) & 0x01FFFFFF;
shift++;
if (value>0) {
nextByte = nextByte + 0b10000000;
}
data.push_back(nextByte);
}
if (value>0) {
data.push_back(value & 0b00001111);
}
}
// appends to the vector
void toVar64(int64_t value, std::vector<uint8_t> &data) {
uint8_t nextByte;
uint8_t shift = 0;
// do something else after the 4th shift
while (shift < 9) {
if (shift>0 && value==0) {
return;
}
nextByte = value & 0b01111111;
value = (value >> 7) & 0x01FFFFFFFFFFFFFF;
shift++;
if (value>0) {
nextByte = nextByte + 0b10000000;
}
data.push_back(nextByte);
}
if (value>0) {
data.push_back(value & 0b00000001);
}
}
}

43
src/lib/net/packet.hpp Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
// BodgeMaster
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#pragma once
#include <vector>
#include <cstdint>
namespace Packet {
struct DataContainer {
uint32_t typeID;
std::vector<uint8_t> payload;
}
namespace TypeID {
// These are guessed names for what the package IDs could mean.
// Some package IDs are used multiple times. I assume the different
// connection states (handshaking, status, login, and play) play a role
// in identifying what kind of package has been received.
// State: handshaking
const uint32_t HANDSHAKE = 0;
// State: status
const uint32_t STATUS = 0;
const uint32_t PING = 1;
}
}

16
src/lib/net/server.cpp Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

16
src/lib/net/server.hpp Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html

View File

@ -177,7 +177,39 @@ int main(){
tiny_utf8::string cutByteString = readFile->readString(readFile->size.value).value; tiny_utf8::string cutByteString = readFile->readString(readFile->size.value).value;
readFile->close(); readFile->close();
ASSERT(modifyFile->cursorPosition == modifyFile->size.value);
ASSERT(cutByte.value == '.'); ASSERT(cutByte.value == '.');
ASSERT(cutByteString == "Hallo, Hi THE CAKE IS A LIE, Ich bin Shwoomple"); ASSERT(cutByteString == "Hallo, Hi THE CAKE IS A LIE, Ich bin Shwoomple");
std::cout << "Passed cut byte test." << std::endl; std::cout << "Passed cut byte test." << std::endl;
modifyFile->open();
modifyFile->cursorPosition = 9;
ErrorOr<std::vector<uint8_t>> cutBytes = modifyFile->cut(18);
modifyFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string cutBytesString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(modifyFile->cursorPosition == 9);
ASSERT(cutBytes.value == std::vector<uint8_t>({' ', 'T', 'H', 'E', ' ', 'C', 'A', 'K', 'E', ' ', 'I', 'S', ' ','A', ' ', 'L', 'I', 'E'}))
ASSERT(cutBytesString == "Hallo, Hi, Ich bin Shwoomple");
std::cout << "Passed cut test." << std::endl;
modifyFile->open();
modifyFile->cursorPosition = 0;
ErrorOr<tiny_utf8::string> cutString = modifyFile->cutString(7);
modifyFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string cutReadString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(modifyFile->cursorPosition == 0);
ASSERT(cutString.value == "Hallo, ");
ASSERT(cutReadString == "Hi, Ich bin Shwoomple");
std::cout << "Passed cutString test." << std::endl;
} }

View File

@ -773,11 +773,16 @@ int main(){
NBT::Tag::Int8* pointer_2_1 = new NBT::Tag::Int8("entry_2_1", 28); NBT::Tag::Int8* pointer_2_1 = new NBT::Tag::Int8("entry_2_1", 28);
NBT::Tag::Int8* pointer_2_2 = new NBT::Tag::Int8("entry_2_2", 45); NBT::Tag::Int8* pointer_2_2 = new NBT::Tag::Int8("entry_2_2", 45);
NBT::Tag::Int8* pointer_2_3 = new NBT::Tag::Int8("entry_2_3", 78); NBT::Tag::Int8* pointer_2_3 = new NBT::Tag::Int8("entry_2_3", 78);
NBT::Tag::Int8* pointer_invalid_0 = new NBT::Tag::Int8("ayya", 78);
NBT::Tag::Int16* pointer_invalid_1 = new NBT::Tag::Int16("ellohhh", 78);
std::vector<NBT::Tag::Generic*> listDataVector; std::vector<NBT::Tag::Generic*> listDataVector;
listDataVector.push_back(pointer_2_0); listDataVector.push_back(pointer_2_0);
listDataVector.push_back(pointer_2_1); listDataVector.push_back(pointer_2_1);
listDataVector.push_back(pointer_2_2); listDataVector.push_back(pointer_2_2);
listDataVector.push_back(pointer_2_3); listDataVector.push_back(pointer_2_3);
std::vector<NBT::Tag::Generic*> invalidListDataVector;
invalidListDataVector.push_back(pointer_invalid_0);
invalidListDataVector.push_back(pointer_invalid_1);
NBT::Tag::List list_0 = NBT::Tag::List(); NBT::Tag::List list_0 = NBT::Tag::List();
ASSERT(list_0.serialize(&vector).isError); ASSERT(list_0.serialize(&vector).isError);
@ -792,8 +797,9 @@ int main(){
NBT::Tag::Int16* pointer_invalid = new NBT::Tag::Int16("invalid", 400); NBT::Tag::Int16* pointer_invalid = new NBT::Tag::Int16("invalid", 400);
resultError = list_1.appendPointer(pointer_invalid); resultError = list_1.appendPointer(pointer_invalid);
ASSERT(resultError.isError && resultError.errorCode == ErrorCodes::INVALID_TYPE); ASSERT(resultError.isError && resultError.errorCode == ErrorCodes::INVALID_TYPE);
//TODO: Check that constructing with a vector of mixed tags ErrorOr<NBT::Tag::List*> invalid_list_or_error = NBT::Tag::List::constructWithData("list_invalid", invalidListDataVector);
// results in a clearly defined failure mode (issue #60) ASSERT(invalid_list_or_error.isError);
ASSERT(invalid_list_or_error.errorCode == ErrorCodes::MIXED_TYPES);
ErrorOr<NBT::Tag::List*> list_2_or_error = NBT::Tag::List::constructWithData("list_2", listDataVector); ErrorOr<NBT::Tag::List*> list_2_or_error = NBT::Tag::List::constructWithData("list_2", listDataVector);
ASSERT(!list_2_or_error.isError); ASSERT(!list_2_or_error.isError);
NBT::Tag::List* list_2 = list_2_or_error.value; NBT::Tag::List* list_2 = list_2_or_error.value;
@ -886,11 +892,24 @@ int main(){
compoundDataVector.push_back(new NBT::Tag::Int16("will be deleted", 0x1337)); compoundDataVector.push_back(new NBT::Tag::Int16("will be deleted", 0x1337));
compoundDataVector.push_back(new NBT::Tag::Int16("1", 0x1337)); compoundDataVector.push_back(new NBT::Tag::Int16("1", 0x1337));
compoundDataVector.push_back(new NBT::Tag::String("2", "Hello World!")); compoundDataVector.push_back(new NBT::Tag::String("2", "Hello World!"));
std::vector<NBT::Tag::Generic*> invalidCompoundDataVector;
invalidCompoundDataVector.push_back(new NBT::Tag::End());
invalidCompoundDataVector.push_back(new NBT::Tag::End());
std::vector<NBT::Tag::Generic*> compoundDataVectorWithEnd;
compoundDataVectorWithEnd.push_back(new NBT::Tag::Int8("eeee", 25));
compoundDataVectorWithEnd.push_back(new NBT::Tag::End());
NBT::Tag::Compound compound_0 = NBT::Tag::Compound(); NBT::Tag::Compound compound_0 = NBT::Tag::Compound();
compound_0.name = "compound_0"; compound_0.name = "compound_0";
NBT::Tag::Compound compound_1 = NBT::Tag::Compound("compound_1"); NBT::Tag::Compound compound_1 = NBT::Tag::Compound("compound_1");
NBT::Tag::Compound compound_2 = NBT::Tag::Compound("compound_2", compoundDataVector); ErrorOr<NBT::Tag::Compound*> invalid_compound_or_error = NBT::Tag::Compound::constructWithData("iiiiii", invalidCompoundDataVector);
ASSERT(invalid_compound_or_error.isError);
ASSERT(invalid_compound_or_error.errorCode == ErrorCodes::NOT_ALLOWED);
ErrorOr<NBT::Tag::Compound*> alternate_compound_or_error = NBT::Tag::Compound::constructWithData("iiiiii", compoundDataVectorWithEnd);
ASSERT(!alternate_compound_or_error.isError);
ErrorOr<NBT::Tag::Compound*> compound_2_or_error = NBT::Tag::Compound::constructWithData("compound_2", compoundDataVector);
ASSERT(!compound_2_or_error.isError);
NBT::Tag::Compound* compound_2 = compound_2_or_error.value;
ASSERT(!compound_1.appendPointer(new NBT::Tag::Int32("0", 69420)).isError); ASSERT(!compound_1.appendPointer(new NBT::Tag::Int32("0", 69420)).isError);
ASSERT(!compound_1.appendPointer(new NBT::Tag::Int8("1", 1)).isError); ASSERT(!compound_1.appendPointer(new NBT::Tag::Int8("1", 1)).isError);
@ -906,15 +925,15 @@ int main(){
resultNotAllowed = compound_1.setElementPointerAt(0, new NBT::Tag::End()); resultNotAllowed = compound_1.setElementPointerAt(0, new NBT::Tag::End());
ASSERT(resultNotAllowed.isError && resultNotAllowed.errorCode==ErrorCodes::NOT_ALLOWED); ASSERT(resultNotAllowed.isError && resultNotAllowed.errorCode==ErrorCodes::NOT_ALLOWED);
ASSERT(compound_0.setElementPointerAt(1, new NBT::Tag::Int8()).isError && compound_0.setElementPointerAt(1, new NBT::Tag::Int8()).errorCode == ErrorCodes::OUT_OF_RANGE); ASSERT(compound_0.setElementPointerAt(1, new NBT::Tag::Int8()).isError && compound_0.setElementPointerAt(1, new NBT::Tag::Int8()).errorCode == ErrorCodes::OUT_OF_RANGE);
ASSERT(!compound_2.deleteElement(1).isError); ASSERT(!compound_2->deleteElement(1).isError);
ASSERT(compound_0.deleteElement(0).isError && compound_0.deleteElement(0).errorCode == ErrorCodes::NOT_ALLOWED); ASSERT(compound_0.deleteElement(0).isError && compound_0.deleteElement(0).errorCode == ErrorCodes::NOT_ALLOWED);
ASSERT(compound_0.deleteElement(1).isError && compound_0.deleteElement(1).errorCode == ErrorCodes::OUT_OF_RANGE); ASSERT(compound_0.deleteElement(1).isError && compound_0.deleteElement(1).errorCode == ErrorCodes::OUT_OF_RANGE);
ASSERT(compound_0.length() == 1); ASSERT(compound_0.length() == 1);
ASSERT(compound_1.length() == 3); ASSERT(compound_1.length() == 3);
ASSERT(compound_2.length() == 4); ASSERT(compound_2->length() == 4);
compound_0.serialize(&vector); compound_0.serialize(&vector);
compound_1.serialize(&vector); compound_1.serialize(&vector);
compound_2.serialize(&vector); compound_2->serialize(&vector);
ASSERT(vector.size() == 95); ASSERT(vector.size() == 95);
ASSERT( ASSERT(
vector.at( 0) == 10 && vector.at( 0) == 10 &&
@ -1016,7 +1035,7 @@ int main(){
vector.clear(); vector.clear();
compound_0.serializeWithoutHeader(&vector); compound_0.serializeWithoutHeader(&vector);
compound_1.serializeWithoutHeader(&vector); compound_1.serializeWithoutHeader(&vector);
compound_2.serializeWithoutHeader(&vector); compound_2->serializeWithoutHeader(&vector);
ASSERT(vector.size() == 56); ASSERT(vector.size() == 56);
ASSERT( ASSERT(
vector.at( 0) == 0 && vector.at( 0) == 0 &&

287
src/test/varint.cpp Normal file
View File

@ -0,0 +1,287 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// Author(s):
// BodgeMaster, Shwoomple
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include <iostream>
#include <cstdint>
#include <vector>
#include "assert.hpp"
#include "../lib/error.hpp"
#include "../lib/net/conversion/varint.hpp"
int main() {
std::cout << "################################################################################" << std::endl;
std::cout << "VarInt tests" << std::endl;
std::cout << "################################################################################" << std::endl;
// examples for numbers
//
// 32 Bit
// 0000 0000 0000 0000 0000 0000 0000 0000 = 0 -> 0000 0000
// 1111 1111 1111 1111 1111 1111 1111 1111 = -1 (unsigned int32 max) -> 1111 1111 1111 1111 1111 1111 1111 1111 0000 1111
// 0000 0000 0010 0100 0011 1101 1011 1101 = 2375101 -> 1011 1101 1111 1011 1001 0000 0000 0001
// 0000 0000 0000 0000 0001 0001 0001 0001 = 4369 -> 1001 0001 0010 0010
//
// 64 Bit
// 0010 0000 0001 0000 0000 0000 1010 0010 1010 1000 0010 0000 1101 0000 1001 0011 = 2310347307446489235 -> 1101 0011 1010 0001 1000 0011 1100 0001 1010 1010 1001 0100 1000 0000 1000 1000 0010 0000
// 1000 0000 0100 0000 0010 0000 0001 0000 0000 1000 0000 0100 0000 0010 0000 0001 = -9205322385119247871 -> 1000 0001 1000 0100 1001 0000 1100 0000 1000 0000 1000 0010 1000 1000 1010 0000 1000 0000 0000 0001
// 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 = -1 (unsigned int64 max) -> 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 0000 0001
{
std::vector<uint8_t> data = std::vector<uint8_t>();
uint8_t processedBytes;
ErrorOr<int32_t> result = VarInt::fromVar32(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OUT_OF_RANGE);
}
{
std::vector<uint8_t> data = { 0x84 };
uint8_t processedBytes;
ErrorOr<int32_t> result = VarInt::fromVar32(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERRUN);
}
{
std::vector<uint8_t> data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
uint8_t processedBytes;
ErrorOr<int32_t> result = VarInt::fromVar32(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERFLOW);
}
uint8_t zeroProcessedBytes = 0;
std::vector<uint8_t> zeroData;
zeroData.push_back(0);
ErrorOr<int32_t> zero = VarInt::fromVar32(zeroData, 0, &zeroProcessedBytes);
ASSERT(!zero.isError);
ASSERT(zero.value == 0);
ASSERT(zeroProcessedBytes == 1);
uint8_t minusOneProcessedBytes = 0;
std::vector<uint8_t> minusOneData;
minusOneData.push_back(255);
minusOneData.push_back(255);
minusOneData.push_back(255);
minusOneData.push_back(255);
minusOneData.push_back(15);
ErrorOr<int32_t> minusOne = VarInt::fromVar32(minusOneData, 0, &minusOneProcessedBytes);
ASSERT(!minusOne.isError);
ASSERT(minusOne.value == -1);
ASSERT(minusOneProcessedBytes == 5);
uint8_t smallProcessedBytes = 0;
std::vector<uint8_t> smallData;
// offset data by 3 to test initialPosition feature
smallData.push_back(0b10010001);
smallData.push_back(0b10010001);
smallData.push_back(0b10010001);
smallData.push_back(0b10010001);
smallData.push_back(0b00100010);
ErrorOr<int32_t> small = VarInt::fromVar32(smallData, 3, &smallProcessedBytes);
ASSERT(!small.isError);
ASSERT(small.value == 4369);
ASSERT(smallProcessedBytes == 2);
uint8_t bigProcessedBytes = 0;
std::vector<uint8_t> bigData;
bigData.push_back(0b10111101);
bigData.push_back(0b11111011);
bigData.push_back(0b10010000);
bigData.push_back(0b00000001);
ErrorOr<int32_t> big = VarInt::fromVar32(bigData, 0, &bigProcessedBytes);
ASSERT(!big.isError);
ASSERT(big.value == 2375101);
ASSERT(bigProcessedBytes == 4);
//TODO: test error conditions
std::cout << "Passed fromVar32 test." << std::endl;
{
std::vector<uint8_t> data = std::vector<uint8_t>();
uint8_t processedBytes;
ErrorOr<int64_t> result = VarInt::fromVar64(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OUT_OF_RANGE);
}
{
std::vector<uint8_t> data = { 0x84 };
uint8_t processedBytes;
ErrorOr<int64_t> result = VarInt::fromVar64(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERRUN);
}
{
std::vector<uint8_t> data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
uint8_t processedBytes;
ErrorOr<int64_t> result = VarInt::fromVar64(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERFLOW);
}
uint8_t zero64ProcessedBytes = 0;
std::vector<uint8_t> zero64Data;
zero64Data.push_back(0);
ErrorOr<int64_t> zero64 = VarInt::fromVar64(zero64Data, 0, &zero64ProcessedBytes);
ASSERT(!zero64.isError);
ASSERT(zero64.value == 0);
ASSERT(zero64ProcessedBytes == 1);
uint8_t minusOne64ProcessedBytes = 0;
std::vector<uint8_t> minusOne64Data;
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(255);
minusOne64Data.push_back(1);
ErrorOr<int64_t> minusOne64 = VarInt::fromVar64(minusOne64Data, 0, &minusOne64ProcessedBytes);
ASSERT(!minusOne64.isError);
ASSERT(minusOne64.value == -1);
ASSERT(minusOne64ProcessedBytes == 10);
// 0010 0000 0001 0000 0000 0000 1010 0010 1010 1000 0010 0000 1101 0000 1001 0011 = 2310347307446489235 -> 1101 0011 1010 0001 1000 0011 1100 0001 1010 1010 1001 0100 1000 0000 1000 1000 0010 0000
uint8_t small64ProcessedBytes = 0;
std::vector<uint8_t> small64Data;
// offset data by 3 to test initialPosition feature
small64Data.push_back(0b11010011);
small64Data.push_back(0b11010011);
small64Data.push_back(0b11010011);
small64Data.push_back(0b10010011);
small64Data.push_back(0b10100001);
small64Data.push_back(0b10000011);
small64Data.push_back(0b11000001);
small64Data.push_back(0b10101010);
small64Data.push_back(0b10010100);
small64Data.push_back(0b10000000);
small64Data.push_back(0b10001000);
small64Data.push_back(0b00100000);
ErrorOr<int64_t> small64 = VarInt::fromVar64(small64Data, 3, &small64ProcessedBytes);
ASSERT(!small64.isError);
ASSERT(small64.value == 2310347307446489235);
ASSERT(small64ProcessedBytes == 9);
// 1000 0000 0100 0000 0010 0000 0001 0000 0000 1000 0000 0100 0000 0010 0000 0001 = -9205322385119247871 -> 1000 0001 1000 0100 1001 0000 1100 0000 1000 0000 1000 0010 1000 1000 1010 0000 1000 0000 0000 0001
uint8_t big64ProcessedBytes = 0;
std::vector<uint8_t> big64Data;
big64Data.push_back(0b10000001);
big64Data.push_back(0b10000100);
big64Data.push_back(0b10010000);
big64Data.push_back(0b11000000);
big64Data.push_back(0b10000000);
big64Data.push_back(0b10000010);
big64Data.push_back(0b10001000);
big64Data.push_back(0b10100000);
big64Data.push_back(0b10000000);
big64Data.push_back(0b00000001);
ErrorOr<int64_t> big64 = VarInt::fromVar64(big64Data, 0, &big64ProcessedBytes);
ASSERT(!big64.isError);
ASSERT(big64.value == -9205322385119247871);
ASSERT(big64ProcessedBytes == 10);
//TODO: Test error conditions
std::cout << "Passed fromVar64 test." << std::endl;
// reversing all the previous tests
std::vector<uint8_t> dataDump;
VarInt::toVar32(0, dataDump);
ASSERT(dataDump[0] == 0);
VarInt::toVar32(-1, dataDump);
ASSERT(
dataDump[1]==255 &&
dataDump[2]==255 &&
dataDump[3]==255 &&
dataDump[4]==255 &&
dataDump[5]==15
);
VarInt::toVar32(4369, dataDump);
ASSERT(
dataDump[6]==0b10010001 &&
dataDump[7]==0b00100010
);
VarInt::toVar32(2375101, dataDump);
ASSERT(
dataDump[8]==0b10111101 &&
dataDump[9]==0b11111011 &&
dataDump[10]==0b10010000 &&
dataDump[11]==0b00000001
);
std::cout << "Passed toVar32 test." << std::endl;
VarInt::toVar64(0, dataDump);
ASSERT(dataDump[12]==0);
VarInt::toVar64(-1, dataDump);
ASSERT(
dataDump[13]==255 &&
dataDump[14]==255 &&
dataDump[15]==255 &&
dataDump[16]==255 &&
dataDump[17]==255 &&
dataDump[18]==255 &&
dataDump[19]==255 &&
dataDump[20]==255 &&
dataDump[21]==255 &&
dataDump[22]==1
);
VarInt::toVar64(2310347307446489235, dataDump);
ASSERT(
dataDump[23]==0b10010011 &&
dataDump[24]==0b10100001 &&
dataDump[25]==0b10000011 &&
dataDump[26]==0b11000001 &&
dataDump[27]==0b10101010 &&
dataDump[28]==0b10010100 &&
dataDump[29]==0b10000000 &&
dataDump[30]==0b10001000 &&
dataDump[31]==0b00100000
);
VarInt::toVar64(-9205322385119247871, dataDump);
ASSERT(
dataDump[32]==0b10000001 &&
dataDump[33]==0b10000100 &&
dataDump[34]==0b10010000 &&
dataDump[35]==0b11000000 &&
dataDump[36]==0b10000000 &&
dataDump[37]==0b10000010 &&
dataDump[38]==0b10001000 &&
dataDump[39]==0b10100000 &&
dataDump[40]==0b10000000 &&
dataDump[41]==0b00000001
)
std::cout << "Passed toVar64 test." << std::endl;
return 0;
}