11/* File: cjlib.c
22 ************************************************************************
3- * Copyright (C) 2024 Constantinos Argyriou
3+ * Copyright (C) 2024-2025 Constantinos Argyriou
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2828#include "cjlib_error.h"
2929#include "cjlib_dictionary.h"
3030#include "cjlib_stack.h"
31- #include "include/cjlib_dictionary.h"
32- #include "include/cjlib_list.h"
33- #include "include/cjlib_queue.h"
34- #include "include/cjlib_stack.h"
31+ #include "cjlib_list.h"
32+ #include "cjlib_queue.h"
3533
3634#define DOUBLE_QUOTES (0x22) // ASCII representative of "
3735#define CURLY_BRACKETS_OPEN (0x7B) // ASCII representative of {
@@ -106,12 +104,17 @@ static inline void incomplete_property_str_init(struct incomplete_property_str *
106104 * @return The new state on success, otherwise NULL.
107105 */
108106static inline char * incomplete_property_str_expand_state
109- (const char * state , const char * data )
107+ (const char * state , const char * data , const char * key )
110108{
111- char * new_state = (char * ) malloc (strlen (state ) + strlen (data ) + 1 );
109+ size_t key_size = (NULL == key )? 0 : strlen (key );
110+ char * new_state = (char * ) malloc (strlen (state ) + strlen (data ) + key_size + 1 );
112111 if (NULL == new_state ) return NULL ;
113-
114- sprintf (new_state , "%s%s" , state , data );
112+
113+ if (NULL == key ) {
114+ sprintf (new_state , "%s%s" , state , data );
115+ } else {
116+ sprintf (new_state , "%s%s:%s" , state , key , data );
117+ }
115118
116119 return new_state ;
117120}
@@ -750,12 +753,11 @@ int cjlib_json_read(struct cjlib_json *restrict dst)
750753 * and produce a string that is in format KEY : DATA,
751754 *
752755 * @param src The data of the field.
753- * @param key The respective key of the field.
754756 * @return A string representing an entry of the JSON file (Format KEY : DATA,) -> (KEY COLON DATA COMMA) in success,
755757 * otherwise NULL is returned.
756758 */
757759static char * simple_key_value_paired_stringtify
758- (const struct cjlib_json_data * restrict src , const char * restrict key )
760+ (const struct cjlib_json_data * restrict src )
759761{
760762 const size_t comma_len = 1 ;
761763 const size_t colon_len = 1 ;
@@ -768,78 +770,29 @@ static char *simple_key_value_paired_stringtify
768770 switch (src -> c_datatype ) {
769771 case CJLIB_STRING :
770772 // LEN(KEY) + LEN(:) + LEN(VALUE) + LEN(,) + LEN("\0")
771- result = (char * ) malloc (strlen (key ) + strlen ( src -> c_value .c_str ) + comma_len + colon_len + 1 );
773+ result = (char * ) malloc (strlen (src -> c_value .c_str ) + comma_len + colon_len + 1 );
772774 if (NULL == result ) return NULL ;
773- sprintf (result , "%s:%s," , key , src -> c_value .c_str );
775+ sprintf (result , "%s," , src -> c_value .c_str );
774776 break ;
775777 case CJLIB_NUMBER :
776778 digit_num = snprintf (NULL , 0 , "%f" , src -> c_value .c_num );
777- result = (char * ) malloc (strlen (key ) + digit_num + comma_len + colon_len + 1 );
778- if (NULL == result ) return NULL ;
779- sprintf (result , "%s:%f," , key , src -> c_value .c_num );
780- break ;
781- case CJLIB_BOOLEAN :
782- if (src -> c_value .c_boolean ) {
783- result = (char * ) malloc (strlen (key ) + boolean_true_len + comma_len + colon_len + 1 );
784- if (NULL == result ) return NULL ;
785- sprintf (result , "%s:%s," , key , "true" );
786- } else {
787- result = (char * ) malloc (strlen (key ) + boolean_false_len + comma_len + colon_len + 1 );
788- if (NULL == result ) return NULL ;
789- sprintf (result , "%s:%s," , key , "false" );
790- }
791- break ;
792- case CJLIB_NULL :
793- result = (char * ) malloc (strlen (key ) + null_len + comma_len + colon_len + 1 );
794- if (NULL == result ) return NULL ;
795- sprintf (result , "%s:%s," , key , "null" );
796- break ;
797- default :
798- break ;
799- }
800-
801- return result ;
802- }
803-
804- /**
805- * Take data from an array as an argument and encode it to the format
806- * of the JSON like array.
807- *
808- * @param src The data of the JSON array.
809- * @return A string representing the format of JSON array data ( DATA, -> DATA COMMA)
810- */
811- static inline char * array_data_strintify (struct cjlib_json_data * restrict src )
812- {
813- const size_t comma_len = 1 ;
814- const size_t boolean_true_len = 4 ;
815- const size_t boolean_false_len = 5 ;
816- const size_t null_len = 4 ;
817- size_t digit_num = 0 ;
818-
819- char * result = NULL ;
820- switch (src -> c_datatype ) {
821- case CJLIB_STRING :
822- result = strdup (src -> c_value .c_str );
823- break ;
824- case CJLIB_NUMBER :
825- digit_num = snprintf (NULL , 0 , "%f" , src -> c_value .c_num );
826- result = (char * ) malloc (digit_num + comma_len + 1 );
779+ result = (char * ) malloc (digit_num + comma_len + colon_len + 1 );
827780 if (NULL == result ) return NULL ;
828781 sprintf (result , "%f," , src -> c_value .c_num );
829782 break ;
830783 case CJLIB_BOOLEAN :
831784 if (src -> c_value .c_boolean ) {
832- result = (char * ) malloc (boolean_true_len + comma_len + 1 );
785+ result = (char * ) malloc (boolean_true_len + comma_len + colon_len + 1 );
833786 if (NULL == result ) return NULL ;
834787 sprintf (result , "%s," , "true" );
835788 } else {
836- result = (char * ) malloc (boolean_false_len + comma_len + 1 );
789+ result = (char * ) malloc (boolean_false_len + comma_len + colon_len + 1 );
837790 if (NULL == result ) return NULL ;
838791 sprintf (result , "%s," , "false" );
839792 }
840793 break ;
841794 case CJLIB_NULL :
842- result = (char * ) malloc (null_len + comma_len + 1 );
795+ result = (char * ) malloc (null_len + comma_len + colon_len + 1 );
843796 if (NULL == result ) return NULL ;
844797 sprintf (result , "%s," , "null" );
845798 break ;
@@ -881,22 +834,51 @@ static inline int switch_incomplete_str_data
881834 return 0 ;
882835}
883836
837+ /**
838+ * Convert the provided @src list into a queue.
839+ *
840+ * The queue consists of pointers that point to the objects
841+ * in the list. (NO NEED FOR FREE FOR THOSE OBJECTS NEEDED).
842+ * This is done for optimization purposes (to allocate and free
843+ * two times the same data).
844+ *
845+ * @param dst A pointer to the queue in which the pointers are stored.
846+ * @param src A pointer to the list from which the paointers will be extracted.
847+ * @return 0 on success, otherwise -1.
848+ */
849+ static int convert_list_to_queue (struct cjlib_queue * restrict dst , const struct cjlib_list * restrict src )
850+ {
851+ struct cjlib_json_data * item ;
852+
853+ CJLIB_LIST_FOR_EACH_PTR (item , src , struct cjlib_json_data ) {
854+ // Store the pointers of each element in the list into the @dst
855+ if ( -1 == cjlib_queue_enqeue ((void * ) & item , sizeof (struct cjlib_json_data * ), dst )) {
856+ return -1 ;
857+ }
858+ }
859+
860+ return 0 ;
861+ }
862+
884863const char * cjlib_json_object_stringtify (const cjlib_json_object * src )
885864{
886865 struct incomplete_property_str curr_incomp ; // The currently expanding data.
887866 struct cjlib_stack incomplete_st ; // The stack of incomplete data.
888867
889868 cjlib_dict_node_t * examine_entry ; // The current key:paired entry from the JSON in memory. (contains both the key and data)
890869 struct cjlib_json_data * examine_entry_data ; // The data of the examined entry.
891-
870+
871+ char * tmp_state = NULL ; // Used to control the memory (see default case in the switch below)
872+ char * tmp_key = NULL ; // Used to temporary store the key of the complete JSON object/array.
873+ char * value_str = NULL ; // Used to temporary store the strintify data (see default case in the switch below)
874+
892875 // Initiazize the incomplete data.
893876 incomplete_property_str_init (& curr_incomp );
894877 cjlib_stack_init (& incomplete_st );
895878
896- switch_incomplete_str_data (& curr_incomp , CJLIB_OBJECT , (cjlib_json_object * ) src );
879+ if ( -1 == switch_incomplete_str_data (& curr_incomp , CJLIB_OBJECT , (cjlib_json_object * ) src )) return NULL ;
897880
898- if (-1 == cjlib_dict_postorder (curr_incomp .i_pending_data_q ,
899- curr_incomp .i_data .object )) return NULL ;
881+ if (-1 == cjlib_dict_postorder (curr_incomp .i_pending_data_q , curr_incomp .i_data .object )) return NULL ;
900882
901883 // Put a dummy entry in the stack to start the process.
902884 if (-1 == cjlib_stack_push ((void * ) & curr_incomp , sizeof (struct incomplete_property_str ),
@@ -906,43 +888,78 @@ const char *cjlib_json_object_stringtify(const cjlib_json_object *src)
906888 if (-1 == cjlib_stack_pop ((void * ) & curr_incomp , sizeof (struct incomplete_property_str ),
907889 & incomplete_st )) return NULL ;
908890
891+ tmp_state = curr_incomp .i_state ;
892+ if (CJLIB_OBJECT == curr_incomp .i_type ) {
893+ curr_incomp .i_state = incomplete_property_str_expand_state (curr_incomp .i_state , value_str , tmp_key );
894+ free (tmp_key );
895+ tmp_key = NULL ;
896+ } else {
897+ curr_incomp .i_state = incomplete_property_str_expand_state (curr_incomp .i_state , value_str , NULL );
898+ }
899+
900+ free (value_str );
901+ free (tmp_state );
902+ tmp_state = NULL ;
903+ value_str = NULL ;
904+
909905 while (!cjlib_queue_is_empty (curr_incomp .i_pending_data_q )) {
910906 // Get the entry to strintify.
911- cjlib_queue_deqeue ((void * ) & examine_entry , sizeof (cjlib_dict_node_t * ), curr_incomp .i_pending_data_q );
912- examine_entry_data = CJLIB_DICT_NODE_DATA (examine_entry );
907+
908+ if (CJLIB_OBJECT == curr_incomp .i_type ) {
909+ // The queue of an incomplete object consists of NODES of avl binary tree.
910+ cjlib_queue_deqeue ((void * ) & examine_entry , sizeof (cjlib_dict_node_t * ), curr_incomp .i_pending_data_q );
911+ examine_entry_data = CJLIB_DICT_NODE_DATA (examine_entry );
912+ } else {
913+ // The queue of an incomplete array consists of NODES of a linked list.
914+ cjlib_queue_deqeue ((void * ) & examine_entry_data , sizeof (struct cjlib_json_data ), curr_incomp .i_pending_data_q );
915+ }
913916
914917 switch (examine_entry_data -> c_datatype ) {
915918 case CJLIB_OBJECT :
916919 cjlib_stack_push (& curr_incomp , sizeof (struct incomplete_property_str ),
917920 & incomplete_st );
918921
919- if (-1 == switch_incomplete_str_data (& curr_incomp , CJLIB_OBJECT ,
920- examine_entry )) return NULL ;
922+ if (-1 == switch_incomplete_str_data (& curr_incomp , CJLIB_OBJECT , examine_entry )) return NULL ;
921923
922- if (-1 == cjlib_dict_postorder (curr_incomp .i_pending_data_q ,
923- curr_incomp .i_data .object )) return NULL ;
924+ if (-1 == cjlib_dict_postorder (curr_incomp .i_pending_data_q , curr_incomp .i_data .object )) return NULL ;
924925 break ;
925926 case CJLIB_ARRAY :
926- cjlib_stack_push (& curr_incomp , sizeof (struct incomplete_property_str ),
927- & incomplete_st );
927+ cjlib_stack_push (& curr_incomp , sizeof (struct incomplete_property_str ), & incomplete_st );
928928
929- if (-1 == switch_incomplete_str_data (& curr_incomp , CJLIB_ARRAY ,
930- examine_entry )) return NULL ;
929+ if (-1 == switch_incomplete_str_data (& curr_incomp , CJLIB_ARRAY , examine_entry )) return NULL ;
931930
932- // TODO - make something similar to postorder, but for arrays, and store
933- // the result in the pending queue.
931+ convert_list_to_queue (curr_incomp .i_pending_data_q , curr_incomp .i_data .array );
934932 break ;
935933 default :
936- // TODO - handle the simple case here (the examine data is not object or array)
934+ tmp_state = curr_incomp .i_state ;
935+
936+ if (CJLIB_OBJECT == curr_incomp .i_type ) {
937+ value_str = simple_key_value_paired_stringtify (CJLIB_DICT_NODE_DATA (examine_entry ));
938+ curr_incomp .i_state = incomplete_property_str_expand_state (curr_incomp .i_state , value_str ,
939+ CJLIB_DICT_NODE_KEY (examine_entry ));
940+ } else {
941+ value_str = simple_key_value_paired_stringtify (examine_entry_data );
942+ curr_incomp .i_state = incomplete_property_str_expand_state (curr_incomp .i_state , value_str , NULL );
943+ }
944+ free (tmp_state );
945+ free (value_str );
946+ tmp_state = NULL ;
947+ value_str = NULL ;
937948 break ;
938949 }
939- // TODO - write a function to wrap an object with {} and an array with [].
940- // TODO - after finishing with the nested while, then free the memory of the current incomplete object
941- // before switching back to the previous incomplete object/array.
942- }
950+ }
951+
952+ value_str = curr_incomp .i_state ; // Save the pointer that points to the state of the complete object/array.
953+ // TODO - wrap the array/object with the correct {} or [].
954+ tmp_key = curr_incomp .i_key ;
955+ // Free the pending queue (there are no more incomplete data for the JSON object/array that were examined).
956+ free (curr_incomp .i_pending_data_q );
957+ curr_incomp .i_pending_data_q = NULL ;
943958 }
944959
945- return NULL ;
960+ free (curr_incomp .i_key );
961+ // Return the now completed JSON.
962+ return curr_incomp .i_state ;
946963}
947964
948965int cjlib_json_dump (const struct cjlib_json * restrict src )
0 commit comments