mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 00:03:11 +01:00
add new C++ memory utility functions
This commit is contained in:
@@ -208,6 +208,9 @@
|
||||
<ClInclude Include="..\src\core\FileStream.hpp" />
|
||||
<ClInclude Include="..\src\core\IDisposable.hpp" />
|
||||
<ClInclude Include="..\src\core\IStream.hpp" />
|
||||
<ClInclude Include="..\src\core\Memory.hpp" />
|
||||
<ClInclude Include="..\src\core\StringBuilder.hpp" />
|
||||
<ClInclude Include="..\src\core\StringReader.hpp" />
|
||||
<ClInclude Include="..\src\cursors.h" />
|
||||
<ClInclude Include="..\src\diagnostic.h" />
|
||||
<ClInclude Include="..\src\drawing\drawing.h" />
|
||||
|
||||
@@ -794,5 +794,14 @@
|
||||
<ClInclude Include="..\src\localisation\LanguagePack.h">
|
||||
<Filter>Source\Localisation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\core\Memory.hpp">
|
||||
<Filter>Source\Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\core\StringBuilder.hpp">
|
||||
<Filter>Source\Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\core\StringReader.hpp">
|
||||
<Filter>Source\Core</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -7,4 +7,6 @@ class Exception : public std::exception {
|
||||
public:
|
||||
Exception() : std::exception() { }
|
||||
Exception(const char *message) : std::exception(message) { }
|
||||
|
||||
const char *GetMessage() const { return what(); }
|
||||
};
|
||||
|
||||
64
src/core/Memory.hpp
Normal file
64
src/core/Memory.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Utility methods for memory management. Typically helpers and wrappers around the C standard library.
|
||||
*/
|
||||
namespace Memory {
|
||||
template<typename T>
|
||||
T *Allocate() {
|
||||
return (T*)malloc(sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *Allocate(size_t size) {
|
||||
return (T*)malloc(size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *AllocateArray(size_t count) {
|
||||
return (T*)malloc(count * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *Reallocate(T *ptr, size_t size) {
|
||||
if (ptr == NULL)
|
||||
return (T*)malloc(size);
|
||||
else
|
||||
return (T*)realloc((void*)ptr, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *ReallocateArray(T *ptr, size_t count) {
|
||||
if (ptr == NULL)
|
||||
return (T*)malloc(count * sizeof(T));
|
||||
else
|
||||
return (T*)realloc((void*)ptr, count * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Free(T *ptr) {
|
||||
free((void*)ptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *Copy(T *dst, const T *src, size_t size) {
|
||||
return (T*)memcpy((void*)dst, (const void*)src, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *CopyArray(T *dst, const T *src, size_t count) {
|
||||
return (T*)memcpy((void*)dst, (const void*)src, count * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *Duplicate(const T *src, size_t size) {
|
||||
T *result = Allocate<T>(size);
|
||||
return Copy(result, src, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *DuplicateArray(const T *src, size_t count) {
|
||||
T *result = AllocateArray<T>(count);
|
||||
return CopyArray(result, src, count);
|
||||
}
|
||||
}
|
||||
82
src/core/StringBuilder.hpp
Normal file
82
src/core/StringBuilder.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../localisation/localisation.h"
|
||||
#include "Memory.hpp"
|
||||
|
||||
/**
|
||||
* Class for constructing strings efficiently. A buffer is automatically allocated and reallocated when characters or strings
|
||||
* are appended. Use GetString to copy the current state of the string builder to a new fire and forget string.
|
||||
*/
|
||||
class StringBuilder final {
|
||||
public:
|
||||
StringBuilder() {
|
||||
_buffer = NULL;
|
||||
_capacity = 0;
|
||||
_length = 0;
|
||||
}
|
||||
|
||||
StringBuilder(int capacity) : StringBuilder() {
|
||||
EnsureCapacity(capacity);
|
||||
}
|
||||
|
||||
~StringBuilder() {
|
||||
if (_buffer != NULL) Memory::Free(_buffer);
|
||||
}
|
||||
|
||||
utf8 *GetString() const {
|
||||
utf8 *result = Memory::AllocateArray<utf8>(_length + 1);
|
||||
Memory::CopyArray(result, _buffer, _length);
|
||||
result[_length] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Append(int codepoint) {
|
||||
int codepointLength = utf8_get_codepoint_length(codepoint);
|
||||
EnsureCapacity(_length + codepointLength + 1);
|
||||
utf8_write_codepoint(_buffer + _length, codepoint);
|
||||
_length += codepointLength;
|
||||
_buffer[_length] = 0;
|
||||
}
|
||||
|
||||
void Append(utf8 *text) {
|
||||
int textLength = strlen(text);
|
||||
|
||||
EnsureCapacity(_length + textLength + 1);
|
||||
Memory::Copy(_buffer + _length, text, textLength);
|
||||
_length += textLength;
|
||||
_buffer[_length] = 0;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
_length = 0;
|
||||
if (_capacity >= 1) {
|
||||
_buffer[_length] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current state of the StringBuilder. Warning: this represents the StringBuilder's current working buffer and will
|
||||
* be deallocated when the StringBuilder is destructed.
|
||||
*/
|
||||
const utf8 *GetBuffer() {
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
utf8 *_buffer;
|
||||
size_t _capacity;
|
||||
size_t _length;
|
||||
|
||||
void EnsureCapacity(size_t capacity)
|
||||
{
|
||||
if (_capacity > capacity) return;
|
||||
|
||||
_capacity = max(8, _capacity);
|
||||
while (_capacity < capacity) {
|
||||
_capacity *= 2;
|
||||
}
|
||||
|
||||
_buffer = Memory::ReallocateArray<utf8>(_buffer, _capacity);
|
||||
}
|
||||
};
|
||||
57
src/core/StringReader.hpp
Normal file
57
src/core/StringReader.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../localisation/localisation.h"
|
||||
#include "../util/util.h"
|
||||
|
||||
interface IStringReader abstract {
|
||||
virtual bool TryPeek(int *outCodepoint) abstract;
|
||||
virtual bool TryRead(int *outCodepoint) abstract;
|
||||
virtual void Skip() abstract;
|
||||
};
|
||||
|
||||
class UTF8StringReader final : public IStringReader {
|
||||
public:
|
||||
UTF8StringReader(const utf8 *text)
|
||||
{
|
||||
// Skip UTF-8 byte order mark
|
||||
if (strlen(text) >= 3 && utf8_is_bom(text)) {
|
||||
text += 3;
|
||||
}
|
||||
|
||||
_text = text;
|
||||
_current = text;
|
||||
}
|
||||
|
||||
bool TryPeek(int *outCodepoint) override
|
||||
{
|
||||
if (_current == NULL) return false;
|
||||
|
||||
int codepoint = utf8_get_next(_current, NULL);
|
||||
*outCodepoint = codepoint;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryRead(int *outCodepoint) override
|
||||
{
|
||||
if (_current == NULL) return false;
|
||||
|
||||
int codepoint = utf8_get_next(_current, &_current);
|
||||
*outCodepoint = codepoint;
|
||||
if (codepoint == 0) {
|
||||
_current = NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Skip() override
|
||||
{
|
||||
int codepoint;
|
||||
TryRead(&codepoint);
|
||||
}
|
||||
|
||||
private:
|
||||
const utf8 *_text;
|
||||
const utf8 *_current;
|
||||
};
|
||||
@@ -4,100 +4,37 @@ extern "C" {
|
||||
#include "localisation.h"
|
||||
}
|
||||
|
||||
#include "../core/FileStream.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../core/StringBuilder.hpp"
|
||||
#include "LanguagePack.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
// TODO Move to separate file
|
||||
class StringBuilder final {
|
||||
public:
|
||||
StringBuilder()
|
||||
{
|
||||
_buffer = NULL;
|
||||
_capacity = 0;
|
||||
_length = 0;
|
||||
}
|
||||
|
||||
StringBuilder(int capacity) : StringBuilder()
|
||||
{
|
||||
EnsureCapacity(capacity);
|
||||
}
|
||||
|
||||
~StringBuilder()
|
||||
{
|
||||
if (_buffer != NULL) free(_buffer);
|
||||
}
|
||||
|
||||
utf8 *GetString() const
|
||||
{
|
||||
utf8 *result = (utf8*)malloc(_length + 1);
|
||||
memcpy(result, _buffer, _length);
|
||||
result[_length] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Append(int codepoint)
|
||||
{
|
||||
int codepointLength = utf8_get_codepoint_length(codepoint);
|
||||
EnsureCapacity(_length + codepointLength + 1);
|
||||
utf8_write_codepoint(_buffer + _length, codepoint);
|
||||
_length += codepointLength;
|
||||
}
|
||||
|
||||
void Append(utf8 *text)
|
||||
{
|
||||
int textLength = strlen(text);
|
||||
|
||||
EnsureCapacity(_length + textLength + 1);
|
||||
memcpy(_buffer + _length, text, textLength);
|
||||
_length += textLength;
|
||||
}
|
||||
|
||||
private:
|
||||
utf8 *_buffer;
|
||||
size_t _capacity;
|
||||
size_t _length;
|
||||
|
||||
void EnsureCapacity(size_t capacity)
|
||||
{
|
||||
if (_capacity > capacity) return;
|
||||
|
||||
_capacity = max(8, _capacity);
|
||||
while (_capacity < capacity) {
|
||||
_capacity *= 2;
|
||||
}
|
||||
|
||||
if (_buffer == NULL) {
|
||||
_buffer = (utf8*)malloc(_capacity);
|
||||
} else {
|
||||
_buffer = (utf8*)realloc(_buffer, _capacity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LanguagePack *LanguagePack::FromFile(int id, const utf8 *path)
|
||||
{
|
||||
assert(path != NULL);
|
||||
|
||||
int fileLength;
|
||||
uint32 fileLength;
|
||||
utf8 *fileData;
|
||||
|
||||
// Load file directly into memory
|
||||
SDL_RWops *file = SDL_RWFromFile(path, "rb");
|
||||
if (file == NULL) {
|
||||
log_error("Unable to open %s", path);
|
||||
try {
|
||||
FileStream fs = FileStream(path, FILE_MODE_OPEN);
|
||||
|
||||
fileLength = (uint32)fs.GetLength();
|
||||
fileData = Memory::Allocate<utf8>(fileLength);
|
||||
fs.Read(fileData, fileLength);
|
||||
|
||||
fs.Dispose();
|
||||
} catch (Exception ex) {
|
||||
log_error("Unable to open %s: %s", path, ex.GetMessage());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fileLength = (int)SDL_RWsize(file);
|
||||
fileData = (utf8*)malloc(fileLength);
|
||||
SDL_RWread(file, fileData, fileLength, 1);
|
||||
SDL_RWclose(file);
|
||||
|
||||
// Parse the memory as text
|
||||
LanguagePack *result = FromText(id, fileData);
|
||||
free(fileData);
|
||||
|
||||
Memory::Free(fileData);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,58 +8,7 @@ extern "C" {
|
||||
#include "localisation.h"
|
||||
}
|
||||
|
||||
struct IStringReader abstract {
|
||||
virtual bool TryPeek(int *outCodepoint) abstract;
|
||||
virtual bool TryRead(int *outCodepoint) abstract;
|
||||
virtual void Skip() abstract;
|
||||
};
|
||||
|
||||
// TODO Move to separate file in Core
|
||||
class UTF8StringReader final : public IStringReader {
|
||||
public:
|
||||
UTF8StringReader(const utf8 *text)
|
||||
{
|
||||
// Skip UTF-8 byte order mark
|
||||
if (strlen(text) >= 3 && utf8_is_bom(text)) {
|
||||
text += 3;
|
||||
}
|
||||
|
||||
_text = text;
|
||||
_current = text;
|
||||
}
|
||||
|
||||
bool TryPeek(int *outCodepoint) override
|
||||
{
|
||||
if (_current == NULL) return false;
|
||||
|
||||
int codepoint = utf8_get_next(_current, NULL);
|
||||
*outCodepoint = codepoint;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryRead(int *outCodepoint) override
|
||||
{
|
||||
if (_current == NULL) return false;
|
||||
|
||||
int codepoint = utf8_get_next(_current, &_current);
|
||||
*outCodepoint = codepoint;
|
||||
if (codepoint == 0) {
|
||||
_current = NULL;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Skip() override
|
||||
{
|
||||
int codepoint;
|
||||
TryRead(&codepoint);
|
||||
}
|
||||
|
||||
private:
|
||||
const utf8 *_text;
|
||||
const utf8 *_current;
|
||||
};
|
||||
#include "../core/StringReader.hpp"
|
||||
|
||||
class LanguagePack final {
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user