Compare commits

...

7 Commits

Author SHA1 Message Date
BodgeMaster b63c1398a6 lib/nbt: Finish implementing containedDataLength, rename nextTagTotalSize->totalTagSize and nextTagDataLength->containedDataLength 2022-08-13 15:58:44 +02:00
BodgeMaster 7bdc3ca7b6 test/nbt_size_helpers: begin adding unit tests for lib/nbt's new nextTag size helpers 2022-08-13 15:48:45 +02:00
BodgeMaster cf0f43ff0e lib/nbt: Begin implementing nextTagDataLength 2022-08-13 15:46:06 +02:00
BodgeMaster 72e1420493 lib/nbt: Fix a bug in nextTagTotalSize and significantly improve readability by removing redundant code 2022-08-13 15:42:52 +02:00
BodgeMaster acd13c94f0 scripts/test: Clean old unit tests before building new ones to avoid confusion
if a unit test fails to build and old unit tests remain in place, this can
lead to confusion when the old test is run anyway.

"Why are some test cases missing?"
"Huh, it failed to build but passed?"
2022-08-13 15:37:25 +02:00
BodgeMaster 8c05223df4 scripts/test/hexnet: Add license information
Yeah, I forgot it again -_-
2022-08-13 13:47:25 +02:00
BodgeMaster f25a5c7025 Build system: Improve output readability and prepare for script based unit tests 2022-08-13 00:29:44 +02:00
10 changed files with 192 additions and 77 deletions

View File

@ -33,7 +33,7 @@ fi
# `.cpp` files in src/lib will be automatically picked up and compiled into
# dynamically linked libraries.
echo "Building libs..."
echo ">>> Building libs..."
create_directory bin/lib
for lib in $(find ./src/lib -name "*.cpp"); do
COMPILE_COMMAND="$CXX_WITH_FLAGS -I ./include -fPIC -shared -o $(sed -e 's/^.\/src/.\/bin/;s/cpp$/so/' <<< $lib) $lib"
@ -55,7 +55,7 @@ wait
# How to run a tool: specify the library path to use for the dynamic linker
# when running a program
# Example: LD_LIBRARY_PATH=bin/lib bin/tools/dumpnbt
echo "Building tools..."
echo ">>> Building tools..."
create_directory bin/tools
# add compile commands to this array
COMPILE_COMMANDS=(

View File

@ -15,6 +15,8 @@
# version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
echo ">>> Cleaning build files..."
source scripts/lib.sh
remove ./bin

View File

@ -17,7 +17,7 @@
source scripts/lib.sh
echo "Checking download cache for unneeded or corrupted files..."
echo ">>> Checking download cache for unneeded or corrupted files..."
for file in $(ls .download_cache); do
#TODO: remove if unknown shasum
if grep $file scripts/setup_project.sh >/dev/null 2>&1; then
@ -32,7 +32,8 @@ for file in $(ls .download_cache); do
rm ".download_cache/$file"
fi
done
echo ""
echo "
>>> Cleaning dependencies..."
remove ./dependencies
create_directory ./dependencies

View File

@ -72,9 +72,7 @@ function download {
fi
}
echo "Cleaning build files..."
scripts/clean.sh
echo "Cleaning dependencies..."
scripts/clean_dependencies.sh
set -e # failures are not acceptable here
@ -82,20 +80,21 @@ create_directory dependencies/tmp
download https://github.com/DuffsDevice/tiny-utf8/archive/refs/tags/v4.4.3.tar.gz dependencies/tmp/tiny-utf8.tar.gz 8e3f61651909c9f3105d3501932a96aa65733127fb6e7cf94cb1b0a2dff42c8f
download https://github.com/fpagliughi/sockpp/archive/refs/tags/v0.7.1.tar.gz dependencies/tmp/sockpp.tar.gz 2e023528bebbd2ac083fc91fbe6d5c4158c3336bedbcff48f594f3b28f53b940
echo -n "Extracting tiny-utf8... "
echo -n ">>> Extracting tiny-utf8... "
gzip -d dependencies/tmp/tiny-utf8.tar.gz
tar -xf dependencies/tmp/tiny-utf8.tar -C dependencies
echo "done."
echo "done"
echo -n "Extracting sockpp... "
echo -n ">>> Extracting sockpp... "
gzip -d dependencies/tmp/sockpp.tar.gz
tar -xf dependencies/tmp/sockpp.tar -C dependencies
echo "done."
echo -n "Building sockpp... "
echo "done"
echo ">>> Building sockpp... "
pushd dependencies/sockpp-0.7.1/ >/dev/null 2>&1
cmake -Bbuild .
cmake --build build
popd >/dev/null 2>&1
echo "done."
echo ">>> Cleaning up..."
remove dependencies/tmp

View File

@ -25,9 +25,11 @@ else
fi
echo "$LD_LIBRARY_PATH"
echo ">>> Cleaning tests..."
remove bin/test
create_directory bin/test
echo "Building tests..."
echo ">>> Building tests..."
# add compile commands to this array
COMPILE_COMMANDS=(
@ -36,6 +38,7 @@ COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/test/javacompat.cpp -I./include -Lbin/lib -l:javacompat.so -o bin/test/javacompat"
"$CXX_WITH_FLAGS src/test/nbt_write_string_failure_mode.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_write_string_failure_mode"
"$CXX_WITH_FLAGS src/test/nbt_tags.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_tags"
"$CXX_WITH_FLAGS src/test/nbt_size_helpers.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_size_helpers"
)
for command in ${!COMPILE_COMMANDS[@]}; do
echo "${COMPILE_COMMANDS[command]}"
@ -45,9 +48,15 @@ done
wait
echo "Running tests..."
echo ">>> Running tests..."
# explicitly allow commands to fail at this stage
set +e
for test in $(ls bin/test); do
bin/test/$test
done
for test in $(ls scripts/test); do
scripts/test/$test
done

23
scripts/test/hexnet.sh Normal file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Copyright 2022, FOSS-VG Developers and Contributers
#
# 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
echo "################################################################################
Testing hexnet
################################################################################"
echo "Test not yet implemented."
#TODO: implement unit test after merging back with master

View File

@ -375,9 +375,11 @@ namespace NBT {
// one that is applicable to the situation (for example replace
// OUT_OF_RANGE with OVERRUN where appropriate)
//
// The total size in bytes
//
// Does not work for compound tags and lists. This is an intended
// feature as compound tags and lists need to be dealt with
// separately to avoid unnecessarily complex code.
// separately to avoid unnecessarily long and complex code.
//
// Regardinng lists specifically: The size of some lists can can
// be determined easily by looking at the contained data type and
@ -386,7 +388,7 @@ namespace NBT {
// contents requires special attention anyway due the tag headers
// of contained tags being absent so they may as well get their
// own function for this as well.
ErrorOr<uint64_t> nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) {
ErrorOr<uint64_t> totalTagSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) {
uint8_t nextTag;
if (dataSize <= currentPosition) {
return ErrorOr<uint64_t>(true, ErrorCodes::OVERRUN);
@ -397,81 +399,53 @@ namespace NBT {
if (nextTag == TagType::COMPOUND || nextTag == TagType::LIST) return ErrorOr<uint64_t>(false, ErrorCodes::NOT_YET_KNOWN);
// deal with end tag before trying to access the name
if (nextTag == TagType::END) return ErrorOr<uint64_t>(1);
// get name size
ErrorOr<int16_t> nameSize = helper::readInt16(data, dataSize, currentPosition+1);
if (nameSize.isError) {
return ErrorOr<uint64_t>(true, nameSize.errorCode);
}
// add type byte and name size bytes
uint64_t prefixSize = (uint64_t) nameSize.value + 3;
switch (nextTag) {
case TagType::INT8:
// type byte + name size + data byte -> 4 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+4);
return ErrorOr<uint64_t>(prefixSize+1);
case TagType::INT16:
// type byte + name size + 2 data bytes -> 5 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+5);
return ErrorOr<uint64_t>(prefixSize+2);
case TagType::INT32:
// type byte + name size + 4 data bytes -> 7 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+7);
return ErrorOr<uint64_t>(prefixSize+4);
case TagType::INT64:
// type byte + name size + 8 data bytes -> 11 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+11);
return ErrorOr<uint64_t>(prefixSize+8);
case TagType::FLOAT:
// type byte + name size + 4 data bytes -> 7 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+7);
return ErrorOr<uint64_t>(prefixSize+4);
case TagType::DOUBLE:
// type byte + name size + 8 data bytes -> 11 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+11);
return ErrorOr<uint64_t>(prefixSize+8);
case TagType::INT8_ARRAY: {
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (1 byte per entry)
ErrorOr<int32_t> arraySize = helper::readInt32(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
ErrorOr<int32_t> arrayLength = helper::readInt32(data, dataSize, currentPosition+prefixSize);
if (arrayLength.isError) {
return ErrorOr<uint64_t>(true, arrayLength.errorCode);
}
totalSize += (uint64_t) arraySize.value;
return ErrorOr<uint64_t>(totalSize);
return ErrorOr<uint64_t>((uint64_t) arrayLength.value + prefixSize + 4);
}
case TagType::STRING: {
// type byte + name size + 2 size bytes -> 5 bytes
uint64_t totalSize = (uint64_t) nameSize.value+5;
// add size of actual data
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+totalSize);
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+prefixSize);
if (stringSize.isError) {
return ErrorOr<uint64_t>(true, stringSize.errorCode);
}
totalSize += (uint64_t) stringSize.value;
return ErrorOr<uint64_t>(totalSize);
return ErrorOr<uint64_t>((uint64_t) stringSize.value + prefixSize + 2);
}
case TagType::INT32_ARRAY: {
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (4 bytes per entry)
ErrorOr<int16_t> arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
ErrorOr<int32_t> arrayLength = helper::readInt32(data, dataSize, currentPosition+prefixSize);
if (arrayLength.isError) {
return ErrorOr<uint64_t>(true, arrayLength.errorCode);
}
totalSize += (uint64_t) arraySize.value*4;
return ErrorOr<uint64_t>(totalSize);
return ErrorOr<uint64_t>((uint64_t) arrayLength.value*4 + prefixSize + 4);
}
case TagType::INT64_ARRAY: {
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (8 bytes per entry)
ErrorOr<int16_t> arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
ErrorOr<int32_t> arrayLength = helper::readInt32(data, dataSize, currentPosition+prefixSize);
if (arrayLength.isError) {
return ErrorOr<uint64_t>(true, arrayLength.errorCode);
}
totalSize += (uint64_t) arraySize.value*8;
return ErrorOr<uint64_t>(totalSize);
return ErrorOr<uint64_t>((uint64_t) arrayLength.value*8 + prefixSize + 4);
}
// unknown tag or parsing error
default:
@ -479,22 +453,67 @@ namespace NBT {
}
}
ErrorOr<uint32_t> nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition){
//FIXME: instead of blindly passing the error code upwards, choose
// one that is applicable to the situation (for example replace
// OUT_OF_RANGE with OVERRUN where appropriate)
//
// Length is the number of stored elements, not to be confused with size
// which is the size in bytes.
ErrorOr<int32_t> containedDataLength(uint8_t data[], uint64_t dataSize, uint64_t currentPosition){
uint8_t nextTag;
if (dataSize <= currentPosition) {
return ErrorOr<uint32_t>(true, ErrorCodes::OVERRUN);
return ErrorOr<int32_t>(true, ErrorCodes::OVERRUN);
} else {
nextTag = data[currentPosition];
}
// deal with compound tags separately
if (nextTag == TagType::COMPOUND) return ErrorOr<uint32_t>(true, ErrorCodes::NOT_YET_KNOWN);
if (nextTag == TagType::COMPOUND) {
return ErrorOr<int32_t>(true, ErrorCodes::NOT_YET_KNOWN);
}
// deal with end tag before trying to access the name
if (nextTag == TagType::END) return 0;
//TODO: implement for all the remaining types
// unknown tag or parsing error
return ErrorOr<uint32_t>(true, ErrorCodes::UNKNOWN);
if (nextTag == TagType::END) {
return ErrorOr<int32_t>(0);
}
// tags that only ever hold one value
if (nextTag == TagType::INT8 || nextTag == TagType::INT16 || nextTag == TagType::INT32 || nextTag == TagType::INT64 || nextTag == TagType::FLOAT || nextTag == TagType::DOUBLE) {
return ErrorOr<int32_t>(1);
}
ErrorOr<int16_t> nameSize = helper::readInt16(data, dataSize, currentPosition+1);
if (nameSize.isError) {
return ErrorOr<int32_t>(true, nameSize.errorCode);
}
// add type byte and name size bytes
uint64_t prefixSize = (uint64_t) nameSize.value + 3;
switch (nextTag) {
case TagType::INT8_ARRAY: {
return helper::readInt32(data, dataSize, currentPosition+prefixSize);
}
case TagType::STRING: {
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+prefixSize);
if (stringSize.isError) {
return ErrorOr<int32_t>(true, stringSize.errorCode);
}
return ErrorOr<int32_t>((int32_t) stringSize.value);
}
case TagType::LIST: {
// add an additional byte for the contained data type
return helper::readInt32(data, dataSize, currentPosition+prefixSize+1);
}
case TagType::INT32_ARRAY: {
return helper::readInt32(data, dataSize, currentPosition+prefixSize);
}
case TagType::INT64_ARRAY: {
return helper::readInt32(data, dataSize, currentPosition+prefixSize);
}
default:
// unknown tag or parsing error
return ErrorOr<int32_t>(true, ErrorCodes::UNKNOWN);
}
}
}

View File

@ -67,8 +67,8 @@ namespace NBT {
void writeInt64Array(std::vector<uint8_t>* destination, std::vector<int64_t> data);
void writeInt64Array(std::vector<uint8_t>* destination, int64_t data[], uint32_t dataSize);
ErrorOr<uint64_t> nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<uint32_t> nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<uint64_t> totalTagSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int32_t> containedDataLength(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
}
namespace TagType {

View File

@ -26,7 +26,7 @@
int main(){
std::cout << "################################################################################" << std::endl;
std::cout << "NBT helper tests" << std::endl;
std::cout << "NBT read/write helper tests" << std::endl;
std::cout << "################################################################################" << std::endl;
// used for all integer tests

View File

@ -0,0 +1,62 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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 "assert.hpp"
#include "../lib/nbt.hpp"
#include "../lib/error.hpp"
int main() {
std::cout
<< "################################################################################\n"
<< "NBT size helper tests\n"
<< "################################################################################"
<< std::endl;
// end tag #########################################################
// const uint8_t END = 0;
uint8_t endTestData[] = {
0x00, 0x0a, 0x00, 0x11, 0x63, 0x6f, 0x6d, 0x70,
0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x6c, 0x65, 0x76,
0x65, 0x6c, 0x5f, 0x32, 0x62, 0x05, 0x00, 0x03,
0x33, 0x30, 0x30, 0x6e, 0x75, 0x6f, 0x70, 0x09,
0x00, 0x03
};
ErrorOr<uint64_t> totalSize = NBT::helper::totalTagSize(endTestData, 34, 0);
ErrorOr<int32_t> dataLength = NBT::helper::containedDataLength(endTestData, 34, 0);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 1);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 0);
//const uint8_t INT8 = 1;
//const uint8_t INT16 = 2;
//const uint8_t INT32 = 3;
//const uint8_t INT64 = 4;
//const uint8_t FLOAT = 5;
//const uint8_t DOUBLE = 6;
//const uint8_t INT8_ARRAY = 7;
//const uint8_t STRING = 8;
//const uint8_t LIST = 9;
//const uint8_t COMPOUND = 10;
//const uint8_t INT32_ARRAY= 11;
//const uint8_t INT64_ARRAY= 12;
return 0;
}