File: msg.c

package info (click to toggle)
golf 601.4.41-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,824 kB
  • sloc: ansic: 20,020; sh: 1,171; makefile: 292
file content (231 lines) | stat: -rw-r--r-- 8,823 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// SPDX-License-Identifier: Apache-2.0
// Copyright 2018-2025 Gliim LLC. 
// Licensed under Apache License v2. See LICENSE file.
// On the web http://golf-lang.com/ - this file is part of Golf framework.

// 
// Message-exchange module
//

#include "golf.h"
// add 38 for ' equals length', and null at the end
#define GG_MSG_ADD_LEN 30 
// buffer size to add when adding items, it increases up to 4K
#ifdef DEBUG
#define GG_MSG_BUFF_LEN 128
#else
#define GG_MSG_BUFF_LEN 1024
#endif
#define GG_MSG_SEP "="
#define GG_MSG_SEP_LEN sizeof(GG_MSG_SEP)-1
void gg_init_msg(gg_msg *t);
char *gg_parse_item (char *item, char **name, gg_num *name_len, char **data, gg_num *data_len, gg_num *st, gg_num len);

//
// Delete message, this is not currently used
//
void gg_del_msg(gg_msg *msg)
{
    GG_TRACE("");
    // we do not delete the 'from' data, i.e. msg->data that is still available, as it should be,
    // or otherwise all pieces of it (values) would become invalid now
    gg_init_msg(msg);
}

//
// get-message uses this to get message (msg) data which it returns
//
char *gg_get_msg (gg_msg *msg)
{
    GG_TRACE("");
    if (msg->data == GG_EMPTY_STRING) return GG_EMPTY_STRING;
    gg_num id = gg_mem_get_id(msg->data);
    gg_mem_set_len (id, msg->len+1);
    // 
    // Once data is taken out, we save the pointer to it, then we set ->data internally to empty string
    // After this, only a new message can be written. This is because once we return this message, if we 
    // continued to build, we would likely realloc, which would invalidate previous pointer, which can now fail
    // if used.
    //
    char *res = msg->data;
    gg_init_msg (msg);
    return res;
}

//
// Write key/value to message msg. The buffer starts with adding 128, then 256, etc  up to 4K, then if total allocd is
// greater than 8x increment, make increment 0.25 of total each time to avoid massive reallocs and copying.
//
void gg_write_msg(gg_msg *msg, char *key, char *value)
{
    GG_TRACE("");
    if (msg->mode != GG_MSG_WRITE) 
    {
        if (msg->mode == GG_MSG_NONE)
        {
            gg_init_msg(msg); msg->mode = GG_MSG_WRITE; // message destroyed and init if it was none or read
        } else gg_report_error ("Once message has been read, it cannot be written to");
    }
    gg_num truelen = gg_mem_get_len(gg_mem_get_id(msg->data));
    if (msg->len > truelen) gg_report_error ("Message is too short to write to, or was deleted"); // the message could have been obtained from get-message and delete-string'd in which
                                               // case truelen will be 0. Trying to access it beyond this would cause SIGSEGV.
    gg_num keyl = gg_mem_get_len (gg_mem_get_id(key));
    gg_num valuel = gg_mem_get_len (gg_mem_get_id(value));
    if (msg->curr == 0)
    {
        msg->addinc = GG_MSG_BUFF_LEN;
        msg->data = gg_malloc (msg->tot = keyl+valuel+GG_MSG_ADD_LEN + msg->addinc); 
    }
    else
    {
        //
        // Because we realloc here, it's a new pointer. Thus when get-message
        // via gg_get_msg(), we can't add any more data! Otherwise any
        // previous get-message would become invalid pointer!
        //
        if (msg->addinc <= 4096) msg->addinc *= 2;
        else if (msg->tot > 8*msg->addinc) msg->addinc = msg->tot/4; 
        if (msg->tot < msg->len+keyl+valuel+GG_MSG_ADD_LEN) msg->data = gg_realloc (gg_mem_get_id(msg->data), msg->tot = msg->len+keyl+valuel+GG_MSG_ADD_LEN + msg->addinc); 
    }
    memcpy (msg->data+msg->curr, key, keyl);
    memcpy (msg->data+msg->curr+keyl, GG_MSG_SEP, GG_MSG_SEP_LEN);
    uint64_t nv =  htobe64((uint64_t)valuel);
    // sizeof(valuel) is 8, which is the same as GG_ALIGN
    memcpy (msg->data+msg->curr+keyl+GG_MSG_SEP_LEN,(unsigned char*)&nv, GG_ALIGN); 
    memcpy (msg->data+msg->curr+keyl+GG_MSG_SEP_LEN+GG_ALIGN,value, valuel+1);
    memcpy (msg->data+msg->curr+keyl+GG_MSG_SEP_LEN+GG_ALIGN+valuel,"\n", 1);
    gg_num final = keyl+GG_MSG_SEP_LEN+GG_ALIGN+valuel+1;
    msg->data[msg->curr+ final] = 0; // finish msg as a string
    msg->len += final;
    msg->curr = msg->len;
    gg_mem_set_len (gg_mem_get_id(msg->data), msg->len+1); // set exact length of message
    return;
}

//
// Key 'comment' is special and ignored.
// Returns GG_ERR_LENGTH if trying to read beyond the end, or GG_ERR_FORMAT if message has bad format.
// key and value are alloced mem, though value is part of msg->data which is the reason there's no delete-message
// Otherwise GG_OKAY returned if got key/value
//
gg_num gg_read_msg(gg_msg *msg, char **key, char **value)
{
    GG_TRACE("");
    if (msg->mode != GG_MSG_READ) {msg->curr = 0; msg->mode = GG_MSG_READ;} // message rewound to beginning if mode none or write
    gg_num truelen = gg_mem_get_len(gg_mem_get_id(msg->data));
    if (msg->len > truelen) return GG_ERR_LENGTH; // the message could have been obtained from get-message and delete-string'd in which
                                               // case truelen will be 0. Trying to access it beyond this would cause SIGSEGV.
    if (msg->curr>=msg->len) return GG_ERR_LENGTH;
    gg_num key_len;
    gg_num value_len;
    gg_num st;
    char *ikey;
    char *ivalue;
    while (1)
    {
        char *next = gg_parse_item (msg->data+msg->curr, &ikey, &key_len, &ivalue, &value_len, &st, msg->len-msg->curr);
        if (st != GG_OKAY) return st;
        else
        {
            // because GG_MSG_SEP has GG_MSG_SEP_LEN bytes, and there's at least @ or one digit before value, we have plus + 1 
            if (key != NULL) *key = gg_strdupl(ikey,0,key_len);
            msg->curr = next - msg->data;
            if (!strcmp (ikey, "comment")) continue; // skip key 'comment'
            if (value != NULL) 
            {
                *value = gg_mem_add_const (ivalue-GG_ALIGN, value_len+1); 
                //*value = gg_strdupl (ivalue,0, value_len);            
            }
            return GG_OKAY;
        }
    }
}


//
// 'item' is parsed, it's of form key=length,value. 'key' cannot have '=' in it, otherwise can be any string that
// doesn't have null in it, including empty. 'length' is a positive number, which can be 0. 'value' is any data, including
// binary with nulls.
// Returns pointer to the next 'item', just passed the current one. 'key' is the key. key_len is its length. 'value' is
// the data and value_len is its length. st is GG_OKAY if okay, or GG_ERR_FORMAT if this simple format is not parsable.
// All pointers are returned as non-golf memory. That's because the programmer can skip some records and just use some, so
// the caller must turn key and equals into golf memory, when requested.
// len is the length of item
//
char *gg_parse_item (char *item, char **key, gg_num *key_len, char **value, gg_num *value_len, gg_num *st, gg_num len)
{
    GG_TRACE("");
    char *beg = item;
    beg[len] = 0; // cap possibilities for out of bounds, such as strstr below
    // get key
    char *eq = strstr (beg, GG_MSG_SEP); 
    if (eq == NULL) { if (st) *st = GG_ERR_FORMAT; return GG_EMPTY_STRING; }
    *key = beg;
    *eq = 0;
    *key_len = eq - *key;
    if (*key_len != 0)
    {
        while (isspace((*key)[0])) {(*key)++; (*key_len)--;}
        if (*key_len != 0)
        {
            gg_num p = -1;
            while (eq+p != *key && isspace(eq[p])) {eq[p--] = 0; (*key_len)--;}
        }
    }
    beg = eq + GG_MSG_SEP_LEN; 
    len -= beg - item;
    //
    char *lvalue;
    if (len < (gg_num)GG_ALIGN) { if (st) *st = GG_ERR_FORMAT; return GG_EMPTY_STRING; }
    // convert length from big endian to local host 64 bit
    uint64_t bl, vl;
    memcpy ((unsigned char*)&bl, beg, GG_ALIGN);
    vl = be64toh(bl);
    len -= GG_ALIGN;
    //
    *value_len = (gg_num)vl;
    if (len < *value_len) { if (st) *st = GG_ERR_LENGTH; return GG_EMPTY_STRING; }
    // get data of length 'len'
    lvalue = beg+GG_ALIGN; // value starts here
    //
    if (lvalue[*value_len] != '\n' && lvalue[*value_len] != 0) { if (st) *st = GG_ERR_LENGTH; return GG_EMPTY_STRING; }
    lvalue[*value_len] = 0;
    *value = lvalue;
    if (st) *st = GG_OKAY;
    return lvalue + *value_len + 1;
}

//
// Creates new message
//
gg_msg * gg_new_msg (char *from)
{
    GG_TRACE("");
    gg_msg *t = gg_malloc(sizeof(gg_msg));
    gg_init_msg(t);
    if (from != NULL)
    {
        // msg cannot be deleted now that it has const mems from it
        gg_num id = gg_mem_get_id (from);
        gg_num l = gg_mem_get_len (id);
        t->data = from;
        t->len = l;
        t->mode = GG_MSG_READ; // once we message 'from' only can read it.
    }
    return t;
}

//
// Init message
//
void gg_init_msg(gg_msg *t)
{
    GG_TRACE("");
    t->data = GG_EMPTY_STRING;
    t->len = 0;
    t->curr = 0;
    t->addinc = GG_MSG_BUFF_LEN;
    t->mode = GG_MSG_NONE;
}