2828#include < fstream>
2929#include < iostream>
3030#include < cctype>
31+ #include < cstdio>
3132
3233static constexpr short TJ_MAX_NUMBER_OF_DIGGITS = 19 ;
3334static 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