Add utils for working with endianness, more documentation, refactor CMake

Decided to update the docs, as well as adding few new features and small
refactoring.
This commit is contained in:
2025-02-28 21:44:01 +03:00
parent 93033ebc99
commit a2d0913c79
19 changed files with 4138 additions and 43 deletions

2
.gitignore vendored
View File

@@ -66,7 +66,7 @@ _deps
.vscode/
# Doxygen
[Dd]ocs/[Hh]tml
[Dd]oc/[Gg]enerated
# Coverage
[Cc]overage

View File

@@ -47,22 +47,25 @@ include(CheckIncludeFile)
include(CheckSymbolExists)
# Unit testing and coverage configuration
set(TESTING ON CACHE BOOL "Enable unit-testing")
set(COVERAGE OFF CACHE BOOL "Enable coverage")
set(ENABLE_TESTING ON CACHE BOOL "Enable unit-testing")
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable coverage")
set(ENABLE_EXAMPLES ON CACHE BOOL "Enable building examples")
set(ENABLE_LTO ON CACHE BOOL "Enable LTO support")
# Check for IPO/LTO
check_ipo_supported(RESULT LTO_SUPPORTED)
if(LTO_SUPPORTED)
message(STATUS "IPO/LTO enabled")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
# Enable IPO/LTO
if(ENABLE_LTO)
check_ipo_supported(RESULT LTO_SUPPORTED)
if(LTO_SUPPORTED)
message(STATUS "IPO/LTO enabled")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
if(TESTING)
# Enable testing
# Enable testing
if(ENABLE_TESTING)
include(CTest)
enable_testing()
endif(TESTING)
endif()
# Set library code
set(BH_SOURCE
@@ -87,6 +90,7 @@ set(BH_SOURCE
src/Math/Vec4f.c
src/Math/Vec4i.c
src/Queue.c
src/Util.c
)
set(BH_HEADER
@@ -97,6 +101,7 @@ set(BH_HEADER
include/BH/IO.h
include/BH/Math.h
include/BH/Queue.h
include/BH/Util.h
)
set(BH_INCLUDE_DIRS
@@ -141,13 +146,17 @@ else()
endif()
# Coverage
if(COVERAGE)
if(ENABLE_COVERAGE)
target_compile_options(BHLib PRIVATE -coverage)
target_link_options(BHLib PRIVATE -coverage)
endif()
# Tests
if(TESTING)
if(ENABLE_TESTING)
add_subdirectory(unit)
add_subdirectory(test)
endif()
if(ENABLE_EXAMPLES)
add_subdirectory(doc/Examples)
endif()

12
README
View File

@@ -1,12 +0,0 @@
BlankHex Library
Library with a bunch of stuff.
Currently implemented:
- I/O device abstraction
- File access (Windows and POSIX)
- Data structures (hashmap, queue)
- Algorithms (swap, sort, partition, heap)
- Geometry math (2D/3D/4D vectors, matrices, quaternions)
- Unit tests

30
README.md Normal file
View File

@@ -0,0 +1,30 @@
# BHLib
Library with a bunch of stuff
## About
Here is a short list of implemented features:
- Abstraction over input/output
- Basic data structures and algorithms (hashmap, queue, heaps, partitions)
- Geomtric primitives (vectors, matrices, quaternions, rays, boxes)
For more information about currently implemented and planned features checkout
[this page](doc/Features.md)
## Docs
The documentation for the functions is provided in the form of Doxygen comments.
The [HowTo guides](doc/HowTo.md) are also available.
## License
BHLib is licensed under [0BSD License](https://opensource.org/license/0bsd).
This means that you can:
- Use this library in personal or commercial projects
- Freely redistribute library in either source or binary forms without
copyright notice
- Modify and relicense your version/fork of the library

2737
doc/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
# PakReader
add_executable(PakReader PakReader.c)
target_link_libraries(PakReader BHLib)

223
doc/Examples/PakReader.c Normal file
View File

@@ -0,0 +1,223 @@
#include <BH/Args.h>
#include <BH/IO.h>
#include <BH/Util.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/* PAK header and entry structures and some constants */
#define HEADER_SIZE 12
#define ENTRY_SIZE 64
typedef struct PakHeader
{
char id[4];
uint32_t offset;
uint32_t size;
} PakHeader;
typedef struct PakEntry
{
char name[56];
uint32_t offset;
uint32_t size;
} PakEntry;
static int ParseHeader(BH_IO *io,
PakHeader *header)
{
char buffer[HEADER_SIZE];
size_t actual;
if (BH_IORead(io, buffer, HEADER_SIZE, &actual) || actual != HEADER_SIZE)
return BH_ERROR;
if (memcmp(buffer, "PACK", 4))
return BH_ERROR;
memcpy(header->id, buffer, 4);
header->offset = BH_Read32LEu(buffer + 4);
header->size = BH_Read32LEu(buffer + 8);
return BH_OK;
}
static int ParseEntry(BH_IO *io,
PakEntry *entry)
{
char buffer[ENTRY_SIZE];
size_t actual;
if (BH_IORead(io, buffer, ENTRY_SIZE, &actual) || actual != ENTRY_SIZE)
return BH_ERROR;
memcpy(entry->name, buffer, 56);
entry->offset = BH_Read32LEu(buffer + 56);
entry->size = BH_Read32LEu(buffer + 60);
entry->name[55] = 0;
return BH_OK;
}
/* Configuration and options */
typedef struct Config
{
char *file;
char *input;
char *output;
int list;
} Config;
static BH_ArgsOption options[] = {
{'h', "help", 0, "Display this help"},
{'l', "list", 0, "List files in the archive instead of reading"},
{'i', "input", BH_ARGS_VALUE, "Input file in archive"},
{'o', "output", BH_ARGS_VALUE, "Output file"},
{0, NULL, 0, NULL}
};
static void PrintHelp(void)
{
printf("Usage: PakReader [options...] <file>\n");
BH_ArgsHelp(options, 0);
}
static int OptionsCallback(int key,
char *arg,
void *data)
{
Config *config = (Config *)data;
switch (key)
{
case BH_ARGS_UNKNOWN: break;
case BH_ARGS_ARGUMENT: if (!config->file) config->file = arg; break;
case 'h': PrintHelp(); exit(0);
case 'l': config->list = 1; break;
case 'i': config->input = arg; break;
case 'o': config->output = arg; break;
}
return BH_OK;
}
/* Copy data between two IO */
static int CopyData(BH_IO *from,
BH_IO *to,
size_t size)
{
size_t i, length, actual;
char tmp[512];
for (i = 0; i < size; i += sizeof(tmp))
{
length = size - i;
if (length > 512)
length = 512;
if (BH_IORead(from, tmp, length, &actual) || length != actual)
return BH_ERROR;
if (BH_IOWrite(to, tmp, length, &actual) || length != actual)
return BH_ERROR;
}
return BH_OK;
}
/* Process pack (list files or extract file) */
static int ProcessPack(Config *config,
BH_IO *io)
{
PakHeader header;
PakEntry entry;
BH_IO *output;
size_t i;
/* Read header and seek to begging of the file table */
if (ParseHeader(io, &header))
return BH_ERROR;
if (BH_IOSeek(io, header.offset, BH_IO_SEEK_SET))
return BH_ERROR;
/* Parse and output entries */
for (i = header.size / 64; i; i--)
{
if (ParseEntry(io, &entry))
return BH_ERROR;
if (config->list)
printf("%s %d\n", entry.name, entry.size);
else
{
if (strcmp(entry.name, config->input))
continue;
output = BH_FileNew(config->output);
if (BH_IOOpen(output, BH_IO_WRITE) ||
BH_IOSeek(io, entry.offset, BH_IO_SEEK_SET) ||
CopyData(io, output, entry.size))
{
BH_IOFree(output);
return BH_ERROR;
}
BH_IOFree(output);
return BH_OK;
}
}
if (config->list)
return BH_OK;
return BH_ERROR;
}
/* Main entry */
int main(int argc, char **argv)
{
Config config;
BH_IO *io;
int result;
/* Parse arguments */
memset(&config, 0, sizeof(config));
if (BH_ArgsParse(argc, argv, options, OptionsCallback, &config) || !config.file)
{
PrintHelp();
return -1;
}
/* Check required arguments */
if (!config.list && (!config.input || !config.output))
{
printf("Specify input and output files\n");
PrintHelp();
return -1;
}
/* Read and write */
io = BH_FileNew(config.file);
if (BH_IOOpen(io, BH_IO_READ | BH_IO_EXIST))
{
printf("Can't open file %s\n", config.file);
BH_IOFree(io);
return -1;
}
result = ProcessPack(&config, io);
BH_IOFree(io);
return result;
}

BIN
doc/Examples/sample.pak Normal file

Binary file not shown.

29
doc/Features.md Normal file
View File

@@ -0,0 +1,29 @@
# Features
## Implemented features
Currently implemented features:
- Abstraction over input/output
- Basic data structures (hashmap, queue)
- Basic algorithms (sorting, swapping, working with heap, partitioning)
- Geomtric primitives (vectors, matrices, quaternions, rays, boxes)
- Intersection calculation (ray, boxes, segments, lines, planes, triangles)
- Unit testing library (for internal usage)
## Planned features
Currently planned features:
- Command-line interface utilities
- Thread support (thread, mutex, cv, atomics, etc.)
- Image loading/processing/saving support
- Polygon rasterization (possibly canvas)
- UTF8 string support (BMP handling)
- Font rendering
- GUI (Windows GUI, X11)
- Deflate/Inflate implementation
- Audio loading/processing/saving support
- FFT/IFFT and complex numbers
- Basic 2D/3D physics engine (possibly as a separate library?)
- Configuration support (INI, JSON, Registry)

6
doc/HowTo.md Normal file
View File

@@ -0,0 +1,6 @@
# HowTo guides
For the time being there is only one HowTo guide:
- [Writing PACK reader utility](HowTo/PakReader.md) which covers the basics of
using IO, Args and Utils modules.

421
doc/HowTo/PakReader.md Normal file
View File

@@ -0,0 +1,421 @@
# HowTo: Reading PACK (pak) Archives
## Overview
In this guide, we will be creating a program called `PakReader`, which will be
able to read PAK archives (list all files within the archive or extract a
specific file from the archive).
## Prerequisites
We want to implement a simple command-line utility that can:
- List the contents of the archive
- Extract a specific file from the archive
To extract the `Text.txt` file from the `sample.pak` archive to `output.txt`, we
would run the following command:
```sh
./PakReader -i Text.txt -o output.txt sample.pak
```
To list files in the `sample.pak` archive, we would run the following command:
```sh
./PakReader -l sample.pak
```
## PACK Archive Format
PACK archives are extremely simple. A PACK archive consists of three parts:
- Header at the beginning of the file
- File table with the name and size of the file
- File data
Header has the following structure:
| Name | Type | Information |
|--------|---------------|-------------------------------------|
| id | 4 byte string | Always contains the word "PACK" |
| offset | uint32 | Offset of the file table (in bytes) |
| size | uint32 | Size of the file table (in bytes) |
File table entry has the following structure:
| Name | Type | Information |
|--------|----------------|-------------------------------------|
| name | 54 byte string | Full file path (null-terminated) |
| offset | uint32 | Offset in the file (in bytes) |
| size | uint32 | Size of the file (in bytes) |
## Includes
To implement this utility, we are going to need to include the following headers:
- `BH/IO.h` to work with files (or input/output devices)
- `BH/Util.h` to read integers with specific width and endianness
- `BH/Args.h` to work with command-line arguments
## Working with Files
Working with files in BHLib is based around the IO device (called `BH_IO`).
Firstly, you need to create an IO device with the `BH_FileNew` function.
Secondly, you need to open the IO device with the `BH_IOOpen` function. While
opening the IO device, you can specify in which mode it will work: reading
(`BH_IO_READ`) or writing (`BH_IO_WRITE`). Additionally, we can specify whether
the IO device (or in our case, the file) should exist before opening
(`BH_IO_EXIST`), be truncated before opening (`BH_IO_TRUNCATE`), should it be
created (`BH_IO_CREATE`), or opened in append mode (`BH_IO_APPEND`).
Here is an example for opening an existing file in read-only mode:
```c
BH_IO *io = BH_FileNew("coolfile.dat");
if (BH_IOOpen(io, BH_IO_READ | BH_IO_EXIST))
{
printf("Can't open file 'coolfile.dat'\n", config.file);
BH_IOFree(io);
return -1;
}
```
## Reading Data from the File
In general, it's best to read some data into a temporary buffer, then parse the
data into variables/structure fields. This makes sure you will not run into
issues caused by misalignment (slowdowns or exceptions/crashes).
For reading or writing integers, it's recommended to use `BH_Read*` and
`BH_Write*`, respectively. Suffixes `16`, `32`, `64` denote integers' width in
bits. Suffixes `LE` and `BE` denote endianness (LittleEndian and BigEndian).
Suffixes `u` and `s` denote unsigned or signed integers.
For example, the function `BH_Read32LEs` reads a signed 32-bit integer that is
stored in LittleEndian format.
Below is an example for reading header data into the structure `header`:
```c
char buffer[HEADER_SIZE];
size_t actual;
if (BH_IORead(io, buffer, HEADER_SIZE, &actual) || actual != HEADER_SIZE)
return BH_ERROR;
if (memcmp(buffer, "PACK", 4))
return BH_ERROR;
memcpy(header->id, buffer, 4);
header->offset = BH_Read32LEu(buffer + 4);
header->size = BH_Read32LEu(buffer + 8);
```
## Working with Command-Line Arguments
Working (or parsing) with CMD arguments is somewhat similar to POSIX
`getopt_long` function. Firstly, you need to define what options (or switches)
are present in your program. To do this, create an array of `BH_ArgsOption`,
filled with option definitions and one empty element at the end of the array.
Each element consists of the following:
- Key
- Name
- Flags
- Description or documentation string
`Key` is used to identify options while processing them. Key value can be an
ASCII character or an integer. If key is an ASCII character, it becomes an
option's short name (key 'd' becomes `-d`).
`Name` is used to represent 'long' option names (name 'hello-world' becomes
`--hello-world`). You can omit `Name`.
`Flags` are used to specify whether an option requires a value (or if they are
optional).
`Description` is used in pair with the function `BH_ArgsHelp` to display
information about the option.
Then you need to define a `callback` function that will do something in response
to the option being parsed.
Array of options, option callback, and ARGC/ARGV is then passed to the function
`BH_ArgsParse`.
Here is the example of using the options:
```c
static BH_ArgsOption options[] = {
{'h', "help", 0, "Display this help"},
{'l', "list", 0, "List files in the archive instead of reading"},
{'i', "input", BH_ARGS_VALUE, "Input file in archive"},
{'o', "output", BH_ARGS_VALUE, "Output file"},
{0, NULL, 0, NULL}
};
static int callback(int key, char *arg, void *data)
{
Config *config = (Config *)data;
switch (key)
{
case BH_ARGS_UNKNOWN: break;
case BH_ARGS_ARGUMENT: if (!config->file) config->file = arg; break;
case 'h': PrintHelp(); exit(0);
case 'l': config->list = 1; break;
case 'i': config->input = arg; break;
case 'o': config->output = arg; break;
}
return BH_OK;
}
int main(int argc, char **argv)
{
Config config;
/* Parse arguments */
memset(&config, 0, sizeof(config));
if (BH_ArgsParse(argc, argv, options, callback, &config) || !config.file)
{
PrintHelp();
return -1;
}
/* ... */
return 0;
}
```
## Putting Everything Together
Now, let's put everything together and implement `PakReader`.
```c
#include <BH/Args.h>
#include <BH/IO.h>
#include <BH/Util.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/* PAK header and entry structures and some constants */
#define HEADER_SIZE 12
#define ENTRY_SIZE 64
typedef struct PakHeader
{
char id[4];
uint32_t offset;
uint32_t size;
} PakHeader;
typedef struct PakEntry
{
char name[56];
uint32_t offset;
uint32_t size;
} PakEntry;
static int ParseHeader(BH_IO *io,
PakHeader *header)
{
char buffer[HEADER_SIZE];
size_t actual;
if (BH_IORead(io, buffer, HEADER_SIZE, &actual) || actual != HEADER_SIZE)
return BH_ERROR;
if (memcmp(buffer, "PACK", 4))
return BH_ERROR;
memcpy(header->id, buffer, 4);
header->offset = BH_Read32LEu(buffer + 4);
header->size = BH_Read32LEu(buffer + 8);
return BH_OK;
}
static int ParseEntry(BH_IO *io,
PakEntry *entry)
{
char buffer[ENTRY_SIZE];
size_t actual;
if (BH_IORead(io, buffer, ENTRY_SIZE, &actual) || actual != ENTRY_SIZE)
return BH_ERROR;
memcpy(entry->name, buffer, 56);
entry->offset = BH_Read32LEu(buffer + 56);
entry->size = BH_Read32LEu(buffer + 60);
entry->name[55] = 0;
return BH_OK;
}
/* Configuration and options */
typedef struct Config
{
char *file;
char *input;
char *output;
int list;
} Config;
static BH_ArgsOption options[] = {
{'h', "help", 0, "Display this help"},
{'l', "list", 0, "List files in the archive instead of reading"},
{'i', "input", BH_ARGS_VALUE, "Input file in archive"},
{'o', "output", BH_ARGS_VALUE, "Output file"},
{0, NULL, 0, NULL}
};
static void PrintHelp(void)
{
printf("Usage: PakReader [options...] <file>\n");
BH_ArgsHelp(options, 0);
}
static int OptionsCallback(int key,
char *arg,
void *data)
{
Config *config = (Config *)data;
switch (key)
{
case BH_ARGS_UNKNOWN: break;
case BH_ARGS_ARGUMENT: if (!config->file) config->file = arg; break;
case 'h': PrintHelp(); exit(0);
case 'l': config->list = 1; break;
case 'i': config->input = arg; break;
case 'o': config->output = arg; break;
}
return BH_OK;
}
/* Copy data between two IO */
static int CopyData(BH_IO *from,
BH_IO *to,
size_t size)
{
size_t i, length, actual;
char tmp[512];
for (i = 0; i < size; i += sizeof(tmp))
{
length = size - i;
if (length > 512)
length = 512;
if (BH_IORead(from, tmp, length, &actual) || length != actual)
return BH_ERROR;
if (BH_IOWrite(to, tmp, length, &actual) || length != actual)
return BH_ERROR;
}
return BH_OK;
}
/* Process pack (list files or extract file) */
static int ProcessPack(Config *config,
BH_IO *io)
{
PakHeader header;
PakEntry entry;
BH_IO *output;
size_t i;
/* Read header and seek to begging of the file table */
if (ParseHeader(io, &header))
return BH_ERROR;
if (BH_IOSeek(io, header.offset, BH_IO_SEEK_SET))
return BH_ERROR;
/* Parse and output entries */
for (i = header.size / 64; i; i--)
{
if (ParseEntry(io, &entry))
return BH_ERROR;
if (config->list)
printf("%s %d\n", entry.name, entry.size);
else
{
if (strcmp(entry.name, config->input))
continue;
output = BH_FileNew(config->output);
if (BH_IOOpen(output, BH_IO_WRITE) ||
BH_IOSeek(io, entry.offset, BH_IO_SEEK_SET) ||
CopyData(io, output, entry.size))
{
BH_IOFree(output);
return BH_ERROR;
}
BH_IOFree(output);
return BH_OK;
}
}
if (config->list)
return BH_OK;
return BH_ERROR;
}
/* Main entry */
int main(int argc, char **argv)
{
Config config;
BH_IO *io;
int result;
/* Parse arguments */
memset(&config, 0, sizeof(config));
if (BH_ArgsParse(argc, argv, options, OptionsCallback, &config) || !config.file)
{
PrintHelp();
return -1;
}
/* Check required arguments */
if (!config.list && (!config.input || !config.output))
{
printf("Specify input and output files\n");
PrintHelp();
return -1;
}
/* Read and write */
io = BH_FileNew(config.file);
if (BH_IOOpen(io, BH_IO_READ | BH_IO_EXIST))
{
printf("Can't open file %s\n", config.file);
BH_IOFree(io);
return -1;
}
result = ProcessPack(&config, io);
BH_IOFree(io);
return result;
}
```

252
include/BH/Util.h Normal file
View File

@@ -0,0 +1,252 @@
#ifndef BH_UTIL_H
#define BH_UTIL_H
#include "Common.h"
/**
* Reads 16-bit unsigned integer from the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 16-bit unsigned integer value.
*/
uint16_t BH_Read16LEu(const char *buffer);
/**
* Reads 16-bit signed integer from the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 16-bit signed integer value.
*/
int16_t BH_Read16LEs(const char *buffer);
/**
* Reads 32-bit unsigned integer from the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 32-bit unsigned integer value.
*/
uint32_t BH_Read32LEu(const char *buffer);
/**
* Reads 32-bit signed integer from the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 32-bit signed integer value.
*/
int32_t BH_Read32LEs(const char *buffer);
/**
* Reads 64-bit unsigned integer from the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 64-bit unsigned integer value.
*/
uint64_t BH_Read64LEu(const char *buffer);
/**
* Reads 64-bit signed integer from the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 64-bit signed integer value.
*/
int64_t BH_Read64LEs(const char *buffer);
/**
* Reads 16-bit unsigned integer from the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 16-bit unsigned integer value.
*/
uint16_t BH_Read16BEu(const char *buffer);
/**
* Reads 16-bit signed integer from the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 16-bit signed integer value.
*/
int16_t BH_Read16BEs(const char *buffer);
/**
* Reads 32-bit unsigned integer from the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 32-bit unsigned integer value.
*/
uint32_t BH_Read32BEu(const char *buffer);
/**
* Reads 32-bit signed integer from the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 32-bit signed integer value.
*/
int32_t BH_Read32BEs(const char *buffer);
/**
* Reads 64-bit unsigned integer from the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 64-bit unsigned integer value.
*/
uint64_t BH_Read64BEu(const char *buffer);
/**
* Reads 64-bit signed integer from the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
*
* \return Returns 64-bit signed integer value.
*/
int64_t BH_Read64BEs(const char *buffer);
/**
* Writes 16-bit unsigned integer to the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write16LEu(char *buffer,
uint16_t value);
/**
* Writes 16-bit signed integer to the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write16LEs(char *buffer,
int16_t value);
/**
* Writes 32-bit unsigned integer to the \a buffer in little-endian format.
*
* \param buffer Buffer pointer\param buffer Buffer pointer
* \param value Value
*/
void BH_Write32LEu(char *buffer,
uint32_t value);
/**
* Writes 32-bit signed integer to the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write32LEs(char *buffer,
int32_t value);
/**
* Writes 64-bit unsigned integer to the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write64LEu(char *buffer,
uint64_t value);
/**
* Writes 64-bit signed integer to the \a buffer in little-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write64LEs(char *buffer,
int64_t value);
/**
* Writes 16-bit unsigned integer to the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write16BEu(char *buffer,
uint16_t value);
/**
* Writes 16-bit signed integer to the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write16BEs(char *buffer,
int16_t value);
/**
* Writes 32-bit unsigned integer to the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write32BEu(char *buffer,
uint32_t value);
/**
* Writes 32-bit signed integer to the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write32BEs(char *buffer,
int32_t value);
/**
* Writes 64-bit unsigned integer to the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write64BEu(char *buffer,
uint64_t value);
/**
* Writes 64-bit signed integer to the \a buffer in big-endian format.
*
* \param buffer Buffer pointer
* \param value Value
*/
void BH_Write64BEs(char *buffer,
int64_t value);
#endif /* BH_UTIL_H */

View File

@@ -1,14 +0,0 @@
Future plans:
- More geometry stuff (boxes, rays, planes, lines, triangles, etc.)
- Thread support (thread, mutex, cv, atomics, etc.)
- Image loading/processing/saving support
- Polygon rasterization (possibly canvas)
- UTF8 string support (BMP handling)
- Font rendering
- GUI (Windows GUI oriented, X11)
- Deflate/Inflate implementation
- Audio loading/processing/saving support
- FFT/IFFT (with basic complex numbers)
- Basic 2D/3D physics engine (possibly as a separate library?)
- Configuration support (INI, JSON, Registry)
- More unit tests

View File

@@ -4,6 +4,7 @@
#define BUFFER_SIZE (sizeof(char *))
struct BH_IO
{
BH_IOCallback cb;

323
src/Util.c Normal file
View File

@@ -0,0 +1,323 @@
#include <BH/Util.h>
#include <string.h>
union I16
{
uint16_t u;
int16_t s;
};
union I32
{
uint32_t u;
int32_t s;
};
union I64
{
uint64_t u;
int64_t s;
};
uint16_t BH_Read16LEu(const char *buffer)
{
union I16 tmp;
tmp.u = (uint16_t)((unsigned char)buffer[0]);
tmp.u |= (uint16_t)((unsigned char)buffer[1]) << 8;
return tmp.u;
}
int16_t BH_Read16LEs(const char *buffer)
{
union I16 tmp;
tmp.u = (uint16_t)((unsigned char)buffer[0]);
tmp.u |= (uint16_t)((unsigned char)buffer[1]) << 8;
return tmp.s;
}
uint32_t BH_Read32LEu(const char *buffer)
{
union I32 tmp;
tmp.u = (uint32_t)((unsigned char)buffer[0]);
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 8;
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 16;
tmp.u |= (uint32_t)((unsigned char)buffer[3]) << 24;
return tmp.u;
}
int32_t BH_Read32LEs(const char *buffer)
{
union I32 tmp;
tmp.u = (uint32_t)((unsigned char)buffer[0]);
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 8;
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 16;
tmp.u |= (uint32_t)((unsigned char)buffer[3]) << 24;
return tmp.s;
}
uint64_t BH_Read64LEu(const char *buffer)
{
union I64 tmp;
tmp.u = (uint64_t)((unsigned char)buffer[0]);
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 8;
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 16;
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 24;
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 32;
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 40;
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 48;
tmp.u |= (uint64_t)((unsigned char)buffer[7]) << 56;
return tmp.u;
}
int64_t BH_Read64LEs(const char *buffer)
{
union I64 tmp;
tmp.u = (uint64_t)((unsigned char)buffer[0]);
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 8;
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 16;
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 24;
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 32;
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 40;
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 48;
tmp.u |= (uint64_t)((unsigned char)buffer[7]) << 56;
return tmp.s;
}
uint16_t BH_Read16BEu(const char *buffer)
{
union I16 tmp;
tmp.u = (uint16_t)((unsigned char)buffer[0]) << 8;
tmp.u |= (uint16_t)((unsigned char)buffer[1]);
return tmp.u;
}
int16_t BH_Read16BEs(const char *buffer)
{
union I16 tmp;
tmp.u = (uint16_t)((unsigned char)buffer[0]) << 8;
tmp.u |= (uint16_t)((unsigned char)buffer[1]);
return tmp.s;
}
uint32_t BH_Read32BEu(const char *buffer)
{
union I32 tmp;
tmp.u = (uint32_t)((unsigned char)buffer[0]) << 24;
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 16;
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 8;
tmp.u |= (uint32_t)((unsigned char)buffer[3]);
return tmp.u;
}
int32_t BH_Read32BEs(const char *buffer)
{
union I32 tmp;
tmp.u = (uint32_t)((unsigned char)buffer[0]) << 24;
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 16;
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 8;
tmp.u |= (uint32_t)((unsigned char)buffer[3]);
return tmp.s;
}
uint64_t BH_Read64BEu(const char *buffer)
{
union I64 tmp;
tmp.u = (uint64_t)((unsigned char)buffer[0]) << 56;
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 48;
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 40;
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 32;
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 24;
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 16;
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 8;
tmp.u |= (uint64_t)((unsigned char)buffer[7]);
return tmp.u;
}
int64_t BH_Read64BEs(const char *buffer)
{
union I64 tmp;
tmp.u = (uint64_t)((unsigned char)buffer[0]) << 56;
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 48;
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 40;
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 32;
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 24;
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 16;
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 8;
tmp.u |= (uint64_t)((unsigned char)buffer[7]);
return tmp.s;
}
void BH_Write16LEu(char *buffer,
uint16_t value)
{
union I16 tmp;
tmp.u = value;
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
}
void BH_Write16LEs(char *buffer,
int16_t value)
{
union I16 tmp;
tmp.s = value;
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
}
void BH_Write32LEu(char *buffer,
uint32_t value)
{
union I32 tmp;
tmp.u = value;
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
}
void BH_Write32LEs(char *buffer,
int32_t value)
{
union I32 tmp;
tmp.s = value;
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
}
void BH_Write64LEu(char *buffer,
uint64_t value)
{
union I64 tmp;
tmp.u = value;
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
((unsigned char *)buffer)[4] = (tmp.u >> 32) & 0xFF;
((unsigned char *)buffer)[5] = (tmp.u >> 40) & 0xFF;
((unsigned char *)buffer)[6] = (tmp.u >> 48) & 0xFF;
((unsigned char *)buffer)[7] = (tmp.u >> 56) & 0xFF;
}
void BH_Write64LEs(char *buffer,
int64_t value)
{
union I64 tmp;
tmp.s = value;
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
((unsigned char *)buffer)[4] = (tmp.u >> 32) & 0xFF;
((unsigned char *)buffer)[5] = (tmp.u >> 40) & 0xFF;
((unsigned char *)buffer)[6] = (tmp.u >> 48) & 0xFF;
((unsigned char *)buffer)[7] = (tmp.u >> 56) & 0xFF;
}
void BH_Write16BEu(char *buffer,
uint16_t value)
{
union I16 tmp;
tmp.u = value;
((unsigned char *)buffer)[0] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u) & 0xFF;
}
void BH_Write16BEs(char *buffer,
int16_t value)
{
union I16 tmp;
tmp.s = value;
((unsigned char *)buffer)[0] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u) & 0xFF;
}
void BH_Write32BEu(char *buffer,
uint32_t value)
{
union I32 tmp;
tmp.u = value;
((unsigned char *)buffer)[0] = (tmp.u >> 24) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u) & 0xFF;
}
void BH_Write32BEs(char *buffer,
int32_t value)
{
union I32 tmp;
tmp.s = value;
((unsigned char *)buffer)[0] = (tmp.u >> 24) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u) & 0xFF;
}
void BH_Write64BEu(char *buffer,
uint64_t value)
{
union I64 tmp;
tmp.u = value;
((unsigned char *)buffer)[0] = (tmp.u >> 56) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 48) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 40) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u >> 32) & 0xFF;
((unsigned char *)buffer)[4] = (tmp.u >> 24) & 0xFF;
((unsigned char *)buffer)[5] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[6] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[7] = (tmp.u) & 0xFF;
}
void BH_Write64BEs(char *buffer,
int64_t value)
{
union I64 tmp;
tmp.s = value;
((unsigned char *)buffer)[0] = (tmp.u >> 56) & 0xFF;
((unsigned char *)buffer)[1] = (tmp.u >> 48) & 0xFF;
((unsigned char *)buffer)[2] = (tmp.u >> 40) & 0xFF;
((unsigned char *)buffer)[3] = (tmp.u >> 32) & 0xFF;
((unsigned char *)buffer)[4] = (tmp.u >> 24) & 0xFF;
((unsigned char *)buffer)[5] = (tmp.u >> 16) & 0xFF;
((unsigned char *)buffer)[6] = (tmp.u >> 8) & 0xFF;
((unsigned char *)buffer)[7] = (tmp.u) & 0xFF;
}

View File

@@ -24,7 +24,7 @@ foreach(TEST_FILENAME ${TEST_FILES})
endif()
# Enable coverage
if(COVERAGE)
if(ENABLE_COVERAGE)
target_compile_options("${TEST_NAME}" PRIVATE -coverage)
target_link_options("${TEST_NAME}" PRIVATE -coverage)
endif()

View File

@@ -385,6 +385,23 @@ BH_UNIT_TEST(InvalidLong)
}
BH_UNIT_TEST(Aesthetics)
{
BH_ArgsOption options[] = {
{'a', "abc", 0, NULL},
{'b', "bcd", BH_ARGS_VALUE, "B option"},
{'i', "input", BH_ARGS_VALUE | BH_ARGS_OPTIONAL, "Input option"},
{1000, "long-option", 0, "Long options\nWith newline"},
{'p', NULL, 0, "P option"},
{0, NULL, 0, NULL},
};
BH_ArgsHelp(options, 0);
return 0;
}
int main(int argc, char **argv)
{
(void)argc;
@@ -398,6 +415,7 @@ int main(int argc, char **argv)
BH_UNIT_ADD(Complex);
BH_UNIT_ADD(InvalidShort);
BH_UNIT_ADD(InvalidLong);
BH_UNIT_ADD(Aesthetics);
return BH_UnitRun();
}

69
test/src/TestEndian.c Normal file
View File

@@ -0,0 +1,69 @@
#include <BH/Util.h>
#include <BH/Unit.h>
BH_UNIT_TEST(Int16)
{
char buffer[2];
BH_Write16LEs(buffer, -12345);
BH_VERIFY(BH_Read16LEs(buffer) == -12345);
BH_Write16BEs(buffer, BH_Read16BEs(buffer));
BH_VERIFY(BH_Read16LEs(buffer) == -12345);
BH_Write16LEu(buffer, 12345);
BH_VERIFY(BH_Read16LEu(buffer) == 12345);
BH_Write16BEu(buffer, BH_Read16BEu(buffer));
BH_VERIFY(BH_Read16LEu(buffer) == 12345);
return 0;
}
BH_UNIT_TEST(Int32)
{
char buffer[4];
BH_Write32LEs(buffer, -12345);
BH_VERIFY(BH_Read32LEs(buffer) == -12345);
BH_Write32BEs(buffer, BH_Read32BEs(buffer));
BH_VERIFY(BH_Read32LEs(buffer) == -12345);
BH_Write32LEu(buffer, 12345);
BH_VERIFY(BH_Read32LEu(buffer) == 12345);
BH_Write32BEu(buffer, BH_Read32BEu(buffer));
BH_VERIFY(BH_Read32LEu(buffer) == 12345);
return 0;
}
BH_UNIT_TEST(Int64)
{
char buffer[8];
BH_Write64LEs(buffer, -12345);
BH_VERIFY(BH_Read64LEs(buffer) == -12345);
BH_Write64BEs(buffer, BH_Read64BEs(buffer));
BH_VERIFY(BH_Read64LEs(buffer) == -12345);
BH_Write64LEu(buffer, 12345);
BH_VERIFY(BH_Read64LEu(buffer) == 12345);
BH_Write64BEu(buffer, BH_Read64BEu(buffer));
BH_VERIFY(BH_Read64LEu(buffer) == 12345);
return 0;
}
int main(int argc, char **argv)
{
BH_UNUSED(argc);
BH_UNUSED(argv);
BH_UNIT_ADD(Int16);
BH_UNIT_ADD(Int32);
BH_UNIT_ADD(Int64);
return BH_UnitRun();
}

View File

@@ -23,7 +23,7 @@ target_include_directories(BHUnit PUBLIC include)
# Enable all compiler warnings
if(MSVC)
target_compile_options(BHUnit PRIVATE /W4 /WX)
target_compile_options(BHUnit PRIVATE /W4 /WX)
else()
target_compile_options(BHUnit PRIVATE -Wall -Wextra -Wpedantic -Werror -fstrict-aliasing)
target_compile_options(BHUnit PRIVATE -Wall -Wextra -Wpedantic -Werror -fstrict-aliasing)
endif()