Skip to content

Commit 4cdd8a8

Browse files
committed
Updated TinyJSON for atomic file support
1 parent 2c24bf1 commit 4cdd8a8

4 files changed

Lines changed: 163 additions & 49 deletions

File tree

src/neuralnetwork/libraries/TinyJSON.cpp

Lines changed: 130 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <fstream>
2929
#include <iostream>
3030
#include <cctype>
31+
#include <cstdio>
3132

3233
static constexpr short TJ_MAX_NUMBER_OF_DIGGITS = 19;
3334
static constexpr short TJ_DEFAULT_STRING_READ_SIZE = 10;
@@ -126,7 +127,7 @@ namespace TinyJSON
126127
struct internal_dump_configuration
127128
{
128129
TJCHAR* _buffer;
129-
const formating _formating;
130+
const formatting _formatting;
130131
const TJCHAR* _indent;
131132
int _buffer_pos;
132133
int _buffer_max_length;
@@ -140,7 +141,7 @@ namespace TinyJSON
140141
bool _has_error;
141142

142143
internal_dump_configuration(
143-
formating formating,
144+
formatting formatting,
144145
const TJCHAR* indent,
145146
const TJCHAR* item_separator,
146147
const TJCHAR* key_separator,
@@ -149,7 +150,7 @@ namespace TinyJSON
149150
const TJCHAR* new_line,
150151
bool escape_special_characters
151152
) :
152-
_formating(formating),
153+
_formatting(formatting),
153154
_indent(indent),
154155
_item_separator(item_separator),
155156
_key_separator(key_separator),
@@ -491,6 +492,24 @@ namespace TinyJSON
491492
{
492493
return index < _number_of_items ? _values[index] : nullptr;
493494
}
495+
496+
/// <summary>
497+
/// Remove an item at a certain position.
498+
/// </summary>
499+
/// <param name="index"></param>
500+
void remove_at(unsigned int index)
501+
{
502+
if (index >= _number_of_items)
503+
{
504+
return;
505+
}
506+
delete _values[index];
507+
if (index < _number_of_items - 1)
508+
{
509+
memmove(&_values[index], &_values[index + 1], (_number_of_items - index - 1) * sizeof(TJValue*));
510+
}
511+
--_number_of_items;
512+
}
494513
private:
495514
/// <summary>
496515
/// The pointers we will take ownership of.
@@ -3900,7 +3919,7 @@ namespace TinyJSON
39003919
/// Write a value to a file.
39013920
/// </summary>
39023921
/// <param name="file_path">The path of the file.</param>
3903-
/// <param name="root">the value we are writting</param>
3922+
/// <param name="root">the value we are writing</param>
39043923
/// <param name="write_options">The options we will be using to write</param>
39053924
/// <returns></returns>
39063925
bool TJ::write_file(const TJCHAR* file_path, const TJValue& root, const write_options& write_options)
@@ -4154,27 +4173,37 @@ namespace TinyJSON
41544173
/// Write a value to a file.
41554174
/// </summary>
41564175
/// <param name="file_path">The path of the file.</param>
4157-
/// <param name="root">the value we are writting</param>
4176+
/// <param name="root">the value we are writing</param>
41584177
/// <param name="write_options">The options we will be using to write</param>
41594178
/// <returns></returns>
41604179
bool TJ::internal_write_file(const TJCHAR* file_path, const TJValue& root, const write_options& write_options)
41614180
{
41624181
WriteResult write_result(write_options);
41634182

41644183
// create the json first before we open anything.
4165-
auto json = root.dump(write_result.options().write_formating);
4184+
auto json = root.dump(write_result.options().write_formatting);
41664185
if (nullptr == json)
41674186
{
41684187
write_result.assign_exception_message("Unable to dump the json.");
41694188
write_result.throw_if_exception();
41704189
return false;
41714190
}
41724191

4173-
// try and optn the file...
4174-
std::ofstream outFile(file_path, std::ios::out | std::ios::binary);
4192+
// Allocate temporary file path
4193+
auto file_path_length = TJHelper::string_length(file_path);
4194+
auto journal_suffix = TJCHARPREFIX("-journal");
4195+
auto journal_length = TJHelper::string_length(journal_suffix);
4196+
TJCHAR* tmp_file_path = new TJCHAR[file_path_length + journal_length + 1];
4197+
TJHelper::copy_string(file_path, tmp_file_path, file_path_length);
4198+
TJHelper::copy_string(journal_suffix, tmp_file_path + file_path_length, journal_length);
4199+
tmp_file_path[file_path_length + journal_length] = TJ_NULL_TERMINATOR;
4200+
4201+
// try and open the temporary file...
4202+
std::ofstream outFile((const char*)tmp_file_path, std::ios::out | std::ios::binary);
41754203
if (!outFile)
41764204
{
4177-
write_result.assign_exception_message("Unable to open file for writting.");
4205+
write_result.assign_exception_message("Unable to open file for writing.");
4206+
delete[] tmp_file_path;
41784207
write_result.throw_if_exception();
41794208
return false;
41804209
}
@@ -4186,6 +4215,7 @@ namespace TinyJSON
41864215
if (!outFile)
41874216
{
41884217
write_result.assign_exception_message("Unable to write UTF-8 BOM.");
4218+
delete[] tmp_file_path;
41894219
write_result.throw_if_exception();
41904220
return false;
41914221
}
@@ -4197,6 +4227,16 @@ namespace TinyJSON
41974227
if (!outFile)
41984228
{
41994229
write_result.assign_exception_message("Unable to write to file.");
4230+
delete[] tmp_file_path;
4231+
write_result.throw_if_exception();
4232+
return false;
4233+
}
4234+
4235+
outFile.flush();
4236+
if (!outFile)
4237+
{
4238+
write_result.assign_exception_message("Unable to flush the file to disk.");
4239+
delete[] tmp_file_path;
42004240
write_result.throw_if_exception();
42014241
return false;
42024242
}
@@ -4206,9 +4246,22 @@ namespace TinyJSON
42064246
if (!outFile)
42074247
{
42084248
write_result.assign_exception_message("Unable to close the file.");
4249+
delete[] tmp_file_path;
42094250
write_result.throw_if_exception();
42104251
return false;
42114252
}
4253+
4254+
// Atomic replace
4255+
std::remove((const char*)file_path);
4256+
if (std::rename((const char*)tmp_file_path, (const char*)file_path) != 0)
4257+
{
4258+
write_result.assign_exception_message("Unable to atomically rename the temporary file to the target file path.");
4259+
delete[] tmp_file_path;
4260+
write_result.throw_if_exception();
4261+
return false;
4262+
}
4263+
4264+
delete[] tmp_file_path;
42124265
return true;
42134266
}
42144267

@@ -4485,14 +4538,14 @@ namespace TinyJSON
44854538
return false;
44864539
}
44874540

4488-
const TJCHAR* TJValue::dump(formating formating, const TJCHAR* indent) const
4541+
const TJCHAR* TJValue::dump(formatting formatting, const TJCHAR* indent) const
44894542
{
44904543
free_last_dump();
4491-
switch (formating)
4544+
switch (formatting)
44924545
{
4493-
case formating::minify:
4546+
case formatting::minify:
44944547
{
4495-
internal_dump_configuration configuration(formating, nullptr,
4548+
internal_dump_configuration configuration(formatting, nullptr,
44964549
TJCHARPREFIX(","),
44974550
TJCHARPREFIX(":"),
44984551
TJCHARPREFIX("\""),
@@ -4506,9 +4559,9 @@ namespace TinyJSON
45064559
_last_dump = configuration._buffer;
45074560
}
45084561
break;
4509-
case formating::indented:
4562+
case formatting::indented:
45104563
{
4511-
internal_dump_configuration configuration(formating, indent,
4564+
internal_dump_configuration configuration(formatting, indent,
45124565
TJCHARPREFIX(","),
45134566
TJCHARPREFIX(": "),
45144567
TJCHARPREFIX("\""),
@@ -4530,7 +4583,7 @@ namespace TinyJSON
45304583
const TJCHAR* TJValue::dump_string() const
45314584
{
45324585
free_last_dump();
4533-
internal_dump_configuration configuration(formating::minify, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, false);
4586+
internal_dump_configuration configuration(formatting::minify, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, false);
45344587
internal_dump(configuration, nullptr);
45354588
if (configuration._has_error)
45364589
{
@@ -5456,7 +5509,7 @@ namespace TinyJSON
54565509
if (number_of_elements > 0)
54575510
{
54585511
// only return if we have data.
5459-
if (configuration._formating == formating::indented)
5512+
if (configuration._formatting == formatting::indented)
54605513
{
54615514
if (!TJHelper::add_char_to_string(TJ_ESCAPE_LINE_FEED, configuration._buffer, configuration._buffer_pos, configuration._buffer_max_length))
54625515
{
@@ -5488,7 +5541,7 @@ namespace TinyJSON
54885541
#endif
54895542
if (member->value()->is_comment())
54905543
{
5491-
if (configuration._formating == formating::minify)
5544+
if (configuration._formatting == formatting::minify)
54925545
{
54935546
// skip comments when minifying
54945547
continue;
@@ -5508,7 +5561,7 @@ namespace TinyJSON
55085561
return;
55095562
}
55105563

5511-
if (configuration._formating == formating::indented)
5564+
if (configuration._formatting == formatting::indented)
55125565
{
55135566
if (!TJHelper::add_char_to_string(TJ_ESCAPE_LINE_FEED, configuration._buffer, configuration._buffer_pos, configuration._buffer_max_length))
55145567
{
@@ -5548,7 +5601,7 @@ namespace TinyJSON
55485601
auto next_it = std::next(it);
55495602
while (next_it != _members->end())
55505603
{
5551-
if (configuration._formating != formating::minify || !(*next_it)->value()->is_comment())
5604+
if (configuration._formatting != formatting::minify || !(*next_it)->value()->is_comment())
55525605
{
55535606
more_items = true;
55545607
break;
@@ -5559,7 +5612,7 @@ namespace TinyJSON
55595612
#else
55605613
for (unsigned int next_i = i + 1; next_i < size; ++next_i)
55615614
{
5562-
if (configuration._formating != formating::minify || !_members->at(next_i)->value()->is_comment())
5615+
if (configuration._formatting != formatting::minify || !_members->at(next_i)->value()->is_comment())
55635616
{
55645617
more_items = true;
55655618
break;
@@ -5576,7 +5629,7 @@ namespace TinyJSON
55765629
return;
55775630
}
55785631
}
5579-
if (configuration._formating == formating::indented)
5632+
if (configuration._formatting == formatting::indented)
55805633
{
55815634
if (!TJHelper::add_char_to_string(TJ_ESCAPE_LINE_FEED, configuration._buffer, configuration._buffer_pos, configuration._buffer_max_length))
55825635
{
@@ -5778,7 +5831,7 @@ namespace TinyJSON
57785831
}
57795832

57805833
delete[] value->_last_dump;
5781-
internal_dump_configuration configuration(formating::minify, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, false);
5834+
internal_dump_configuration configuration(formatting::minify, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, false);
57825835
value->internal_dump(configuration, nullptr);
57835836
if (configuration._has_error)
57845837
{
@@ -5941,7 +5994,7 @@ namespace TinyJSON
59415994
#endif
59425995
if (value->is_comment())
59435996
{
5944-
if (configuration._formating == formating::minify)
5997+
if (configuration._formatting == formatting::minify)
59455998
{
59465999
// skip comments when minifying
59476000
continue;
@@ -5993,7 +6046,7 @@ namespace TinyJSON
59936046
auto next_it = std::next(it);
59946047
while (next_it != _values->end())
59956048
{
5996-
if (configuration._formating != formating::minify || !(*next_it)->is_comment())
6049+
if (configuration._formatting != formatting::minify || !(*next_it)->is_comment())
59976050
{
59986051
more_items = true;
59996052
break;
@@ -6004,7 +6057,7 @@ namespace TinyJSON
60046057
#else
60056058
for (unsigned int next_i = i + 1; next_i < size; ++next_i)
60066059
{
6007-
if (configuration._formating != formating::minify || !_values->at(next_i)->is_comment())
6060+
if (configuration._formatting != formatting::minify || !_values->at(next_i)->is_comment())
60086061
{
60096062
more_items = true;
60106063
break;
@@ -6192,10 +6245,17 @@ namespace TinyJSON
61926245

61936246
std::vector<long double> TJValueArray::get_floats() const
61946247
{
6248+
const unsigned int count = get_number_of_items();
61956249
std::vector<long double> values = {};
6196-
for (unsigned int i = 0; i < get_number_of_items(); ++i)
6250+
values.reserve(count);
6251+
const unsigned int elements_count = get_number_of_elements();
6252+
for (unsigned int i = 0; i < elements_count; ++i)
61976253
{
6198-
auto value = at(i);
6254+
auto* value = element_at(i);
6255+
if (value->is_comment())
6256+
{
6257+
continue;
6258+
}
61996259
if (!value->is_number())
62006260
{
62016261
ParseResult _parse_result(_parse_options);
@@ -6210,10 +6270,17 @@ namespace TinyJSON
62106270

62116271
std::vector<long long> TJValueArray::get_numbers() const
62126272
{
6273+
const unsigned int count = get_number_of_items();
62136274
std::vector<long long> values = {};
6214-
for (unsigned int i = 0; i < get_number_of_items(); ++i)
6275+
values.reserve(count);
6276+
const unsigned int elements_count = get_number_of_elements();
6277+
for (unsigned int i = 0; i < elements_count; ++i)
62156278
{
6216-
auto value = at(i);
6279+
auto* value = element_at(i);
6280+
if (value->is_comment())
6281+
{
6282+
continue;
6283+
}
62176284
if (!value->is_number())
62186285
{
62196286
ParseResult _parse_result(_parse_options);
@@ -6298,6 +6365,38 @@ namespace TinyJSON
62986365
add_move(objectString);
62996366
}
63006367

6368+
void TJValueArray::remove_at(unsigned int index)
6369+
{
6370+
if (index >= get_number_of_items())
6371+
{
6372+
return;
6373+
}
6374+
6375+
unsigned int element_index = 0;
6376+
unsigned int current_item_index = 0;
6377+
auto size = _values->size();
6378+
for (unsigned int i = 0; i < size; ++i)
6379+
{
6380+
if (!_values->at(i)->is_comment())
6381+
{
6382+
if (current_item_index == index)
6383+
{
6384+
element_index = i;
6385+
break;
6386+
}
6387+
current_item_index++;
6388+
}
6389+
}
6390+
6391+
#if TJ_INCLUDE_STDVECTOR == 1
6392+
delete (*_values)[element_index];
6393+
_values->erase(_values->begin() + element_index);
6394+
#else
6395+
_values->remove_at(element_index);
6396+
#endif
6397+
TJNumberedValues::reset_number_of_items();
6398+
}
6399+
63016400
///////////////////////////////////////
63026401
/// TJValue Number
63036402
TJValueNumber::TJValueNumber(const bool is_negative, const parse_options & options) :
@@ -6919,7 +7018,7 @@ namespace TinyJSON
69197018
void TJValueComment::internal_dump(internal_dump_configuration& configuration, const TJCHAR* current_indent) const
69207019
{
69217020
// If we are minifying, we don't want comments.
6922-
if (configuration._formating == formating::minify)
7021+
if (configuration._formatting == formatting::minify)
69237022
{
69247023
return;
69257024
}

0 commit comments

Comments
 (0)