aboutsummaryrefslogtreecommitdiffstats
path: root/src/vlib/lex.c
blob: 1cc8f1678d27f9b8b847cd08e08985ee85cc1b17 (plain)
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vlib/vlib.h>
#include <vlib/lex.h>

vlib_lex_main_t vlib_lex_main;

#define LEX_DEBUG 0

u8 *
format_vlib_lex_token (u8 * s, va_list * args)
{
  vlib_lex_main_t *lm = va_arg (*args, vlib_lex_main_t *);
  vlib_lex_token_t *t = va_arg (*args, vlib_lex_token_t *);

  if (t->token == VLIB_LEX_word)
    s = format (s, "%s", t->value.as_pointer);
  else
    s = format (s, "%s", lm->lex_token_names[t->token]);
  return s;
}

void
vlib_lex_get_token (vlib_lex_main_t * lm, vlib_lex_token_t * rv)
{
  u8 c;
  vlib_lex_table_t *t;
  vlib_lex_table_entry_t *e;
  uword tv;

  if (PREDICT_FALSE (lm->pushback_sp >= 0))
    {
      rv[0] = lm->pushback_vector[lm->pushback_sp--];
      return;
    }

  rv->value.as_uword = ~0;

  while (1)
    {
      if (PREDICT_FALSE (lm->current_index >= vec_len (lm->input_vector)))
	{
	  rv->token = VLIB_LEX_eof;
	  return;
	}

      t = vec_elt_at_index (lm->lex_tables, lm->current_table_index);
      c = (lm->input_vector[lm->current_index++]) & 0x7f;
      e = &t->entries[c];
      lm->current_table_index = e->next_table_index;

      switch (e->action)
	{
	case VLIB_LEX_IGNORE:
	  continue;

	case VLIB_LEX_START_NUMBER:
	  lm->current_token_value = 0;
	  /* fallthru */

	case VLIB_LEX_ADD_TO_NUMBER:
	  lm->current_number_base = e->token;
	  lm->current_token_value *= lm->current_number_base;
	  tv = c - '0';
	  if (tv >= lm->current_number_base)
	    {
	      tv = 10 + c - 'A';
	      if (tv >= lm->current_number_base)
		tv = 10 + c - 'a';
	    }
	  lm->current_token_value += tv;
	  continue;

	case VLIB_LEX_ADD_TO_TOKEN:
	  vec_add1 (lm->token_buffer, c);
	  continue;

	case VLIB_LEX_KEYWORD_CHECK:
	  {
	    uword *p;

	    vec_add1 (lm->token_buffer, 0);

	    /* It's either a keyword or just a word. */
	    p = hash_get_mem (lm->lex_keywords, lm->token_buffer);
	    if (p)
	      {
		rv->token = p[0];
		if (LEX_DEBUG > 0)
		  clib_warning ("keyword '%s' token %s",
				lm->token_buffer,
				lm->lex_token_names[rv->token]);
	      }
	    else
	      {
		/* it's a WORD */
		rv->token = VLIB_LEX_word;
		rv->value.as_pointer = vec_dup (lm->token_buffer);
		if (LEX_DEBUG > 0)
		  clib_warning ("%s, value '%s'",
				lm->lex_token_names[VLIB_LEX_word],
				rv->value.as_pointer);
	      }
	    _vec_len (lm->token_buffer) = 0;

	    /* Rescan the character which terminated the keyword/word. */
	    lm->current_index--;
	    return;
	  }

	case VLIB_LEX_RETURN_AND_RESCAN:
	  ASSERT (lm->current_index);
	  lm->current_index--;
	  /* note flow-through */

	case VLIB_LEX_RETURN:
	  rv->token = e->token;
	  rv->value.as_uword = lm->current_token_value;
	  lm->current_token_value = ~0;
	  if (LEX_DEBUG > 0)
	    {
	      clib_warning
		("table %s char '%c'(0x%02x) next table %s return %s",
		 t->name, c, c, lm->lex_tables[e->next_table_index].name,
		 lm->lex_token_names[e->token]);
	      if (rv->token == VLIB_LEX_number)
		clib_warning ("  numeric value 0x%x (%d)", rv->value,
			      rv->value);
	    }
	  return;
	}
    }
}

u16
vlib_lex_add_token (vlib_lex_main_t * lm, char *token_name)
{
  uword *p;
  u16 rv;

  p = hash_get_mem (lm->lex_tokens_by_name, token_name);

  if (p)
    return p[0];

  rv = vec_len (lm->lex_token_names);
  hash_set_mem (lm->lex_tokens_by_name, token_name, rv);
  vec_add1 (lm->lex_token_names, token_name);

  return rv;
}

static u16
add_keyword (vlib_lex_main_t * lm, char *keyword, char *token_name)
{
  uword *p;
  u16 token;

  p = hash_get_mem (lm->lex_keywords, keyword);

  ASSERT (p == 0);

  token = vlib_lex_add_token (lm, token_name);

  hash_set_mem (lm->lex_keywords, keyword, token);
  return token;
}

u16
vlib_lex_find_or_add_keyword (vlib_lex_main_t * lm, char *keyword,
			      char *token_name)
{
  uword *p = hash_get_mem (lm->lex_keywords, keyword);
  return p ? p[0] : add_keyword (lm, keyword, token_name);
}

void
vlib_lex_set_action_range (u32 table_index, u8 lo, u8 hi, u16 action,
			   u16 token, u32 next_table_index)
{
  int i;
  vlib_lex_main_t *lm = &vlib_lex_main;
  vlib_lex_table_t *t = pool_elt_at_index (lm->lex_tables, table_index);

  for (i = lo; i <= hi; i++)
    {
      ASSERT (i < ARRAY_LEN (t->entries));
      t->entries[i].action = action;
      t->entries[i].token = token;
      t->entries[i].next_table_index = next_table_index;
    }
}

u16
vlib_lex_add_table (char *name)
{
  vlib_lex_main_t *lm = &vlib_lex_main;
  vlib_lex_table_t *t;
  uword *p;

  p = hash_get_mem (lm->lex_tables_by_name, name);

  ASSERT (p == 0);

  pool_get_aligned (lm->lex_tables, t, CLIB_CACHE_LINE_BYTES);

  t->name = name;

  hash_set_mem (lm->lex_tables_by_name, name, t - lm->lex_tables);

  vlib_lex_set_action_range (t - lm->lex_tables, 1, 0x7F, VLIB_LEX_IGNORE, ~0,
			     t - lm->lex_tables);

  vlib_lex_set_action_range (t - lm->lex_tables, 0, 0, VLIB_LEX_RETURN,
			     VLIB_LEX_eof, t - lm->lex_tables);

  return t - lm->lex_tables;
}

void
vlib_lex_reset (vlib_lex_main_t * lm, u8 * input_vector)
{
  if (lm->pushback_vector)
    _vec_len (lm->pushback_vector) = 0;
  lm->pushback_sp = -1;

  lm->input_vector = input_vector;
  lm->current_index = 0;
}

static clib_error_t *
lex_onetime_init (vlib_main_t * vm)
{
  vlib_lex_main_t *lm = &vlib_lex_main;

  lm->lex_tables_by_name = hash_create_string (0, sizeof (uword));
  lm->lex_tokens_by_name = hash_create_string (0, sizeof (uword));
  lm->lex_keywords = hash_create_string (0, sizeof (uword));
  lm->pushback_sp = -1;

#define _(f) { u16 tmp = vlib_lex_add_token (lm, #f); ASSERT (tmp == VLIB_LEX_##f); }
  foreach_vlib_lex_global_token;
#undef _

  vec_validate (lm->token_buffer, 127);
  _vec_len (lm->token_buffer) = 0;

  return 0;
}

VLIB_INIT_FUNCTION (lex_onetime_init);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
ass="p">; return vlib_buffer_get_current (b); } /** \brief Make head room, typically for packet headers * @param b pointer to the buffer * @param size number of head room bytes * @return pointer to start of buffer (current data) */ always_inline void * vlib_buffer_make_headroom (vlib_buffer_t * b, u8 size) { ASSERT (b->current_data + VLIB_BUFFER_PRE_DATA_SIZE >= size); b->current_data += size; return vlib_buffer_get_current (b); } /** \brief Retrieve bytes from buffer head * @param b pointer to the buffer * @param size number of bytes to pull * @return pointer to start of buffer (current data) */ always_inline void * vlib_buffer_pull (vlib_buffer_t * b, u8 size) { if (b->current_length + VLIB_BUFFER_PRE_DATA_SIZE < size) return 0; void *data = vlib_buffer_get_current (b); vlib_buffer_advance (b, size); return data; } /* Forward declaration. */ struct vlib_main_t; typedef struct vlib_buffer_free_list_t { /* Template buffer used to initialize first 16 bytes of buffers allocated on this free list. */ vlib_buffer_t buffer_init_template; /* Our index into vlib_main_t's buffer_free_list_pool. */ u32 index; /* Number of data bytes for buffers in this free list. */ u32 n_data_bytes; /* Number of buffers to allocate when we need to allocate new buffers from physmem heap. */ u32 min_n_buffers_each_physmem_alloc; /* Total number of buffers allocated from this free list. */ u32 n_alloc; /* Vector of free buffers. Each element is a byte offset into I/O heap. */ u32 *buffers; /* global vector of free buffers, used only on main thread. Bufers are returned to global buffers only in case when number of buffers on free buffers list grows about threshold */ u32 *global_buffers; clib_spinlock_t global_buffers_lock; /* Memory chunks allocated for this free list recorded here so they can be freed when free list is deleted. */ void **buffer_memory_allocated; /* Free list name. */ u8 *name; /* Callback functions to initialize newly allocated buffers. If null buffers are zeroed. */ void (*buffer_init_function) (struct vlib_main_t * vm, struct vlib_buffer_free_list_t * fl, u32 * buffers, u32 n_buffers); /* Callback function to announce that buffers have been added to the freelist */ void (*buffers_added_to_freelist_function) (struct vlib_main_t * vm, struct vlib_buffer_free_list_t * fl); uword buffer_init_function_opaque; } __attribute__ ((aligned (16))) vlib_buffer_free_list_t; typedef struct { u32 (*vlib_buffer_alloc_cb) (struct vlib_main_t * vm, u32 * buffers, u32 n_buffers); u32 (*vlib_buffer_alloc_from_free_list_cb) (struct vlib_main_t * vm, u32 * buffers, u32 n_buffers, u32 free_list_index); void (*vlib_buffer_free_cb) (struct vlib_main_t * vm, u32 * buffers, u32 n_buffers); void (*vlib_buffer_free_no_next_cb) (struct vlib_main_t * vm, u32 * buffers, u32 n_buffers); void (*vlib_packet_template_init_cb) (struct vlib_main_t * vm, void *t, void *packet_data, uword n_packet_data_bytes, uword min_n_buffers_each_physmem_alloc, u8 * name); void (*vlib_buffer_delete_free_list_cb) (struct vlib_main_t * vm, u32 free_list_index); } vlib_buffer_callbacks_t; extern vlib_buffer_callbacks_t *vlib_buffer_callbacks; typedef struct { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); /* Virtual memory address and size of buffer memory, used for calculating buffer index */ uword buffer_mem_start; uword buffer_mem_size; /* Buffer free callback, for subversive activities */ u32 (*buffer_free_callback) (struct vlib_main_t * vm, u32 * buffers, u32 n_buffers, u32 follow_buffer_next); /* Pool of buffer free lists. Multiple free lists exist for packet generator which uses separate free lists for each packet stream --- so as to avoid initializing static data for each packet generated. */ vlib_buffer_free_list_t *buffer_free_list_pool; #define VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX (0) #define VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES VLIB_BUFFER_DATA_SIZE /* Hash table mapping buffer size (rounded to next unit of sizeof (vlib_buffer_t)) to free list index. */ uword *free_list_by_size; /* Hash table mapping buffer index into number 0 => allocated but free, 1 => allocated and not-free. If buffer index is not in hash table then this buffer has never been allocated. */ uword *buffer_known_hash; clib_spinlock_t buffer_known_hash_lockp; /* List of free-lists needing Blue Light Special announcements */ vlib_buffer_free_list_t **announce_list; /* Callbacks */ vlib_buffer_callbacks_t cb; int callbacks_registered; } vlib_buffer_main_t; void vlib_buffer_add_mem_range (struct vlib_main_t *vm, uword start, uword size); void vlib_buffer_cb_init (struct vlib_main_t *vm); typedef struct { struct vlib_main_t *vlib_main; u32 first_buffer, last_buffer; union { struct { /* Total accumulated bytes in chain starting with first_buffer. */ u32 n_total_data_bytes; /* Max number of bytes to accumulate in chain starting with first_buffer. As this limit is reached buffers are enqueued to next node. */ u32 max_n_data_bytes_per_chain; /* Next node to enqueue buffers to relative to current process node. */ u32 next_index; /* Free list to use to allocate new buffers. */ u32 free_list_index; } tx; struct { /* CLIB fifo of buffer indices waiting to be unserialized. */ u32 *buffer_fifo; /* Event type used to signal that RX buffers have been added to fifo. */ uword ready_one_time_event; } rx; }; } vlib_serialize_buffer_main_t; void serialize_open_vlib_buffer (serialize_main_t * m, struct vlib_main_t *vm, vlib_serialize_buffer_main_t * sm); void unserialize_open_vlib_buffer (serialize_main_t * m, struct vlib_main_t *vm, vlib_serialize_buffer_main_t * sm); u32 serialize_close_vlib_buffer (serialize_main_t * m); void unserialize_close_vlib_buffer (serialize_main_t * m); void *vlib_set_buffer_free_callback (struct vlib_main_t *vm, void *fp); always_inline u32 serialize_vlib_buffer_n_bytes (serialize_main_t * m) { serialize_stream_t *s = &m->stream; vlib_serialize_buffer_main_t *sm = uword_to_pointer (m->stream.data_function_opaque, vlib_serialize_buffer_main_t *); return sm->tx.n_total_data_bytes + s->current_buffer_index + vec_len (s->overflow_buffer); } /* */ /** \brief Compile time buffer trajectory tracing option Turn this on if you run into "bad monkey" contexts, and you want to know exactly which nodes they've visited... See vlib/main.c... */ #define VLIB_BUFFER_TRACE_TRAJECTORY 0 #if VLIB_BUFFER_TRACE_TRAJECTORY > 0 #define VLIB_BUFFER_TRACE_TRAJECTORY_INIT(b) (b)->pre_data[0]=0 #else #define VLIB_BUFFER_TRACE_TRAJECTORY_INIT(b) #endif /* VLIB_BUFFER_TRACE_TRAJECTORY */ #endif /* included_vlib_buffer_h */ #define VLIB_BUFFER_REGISTER_CALLBACKS(x,...) \ __VA_ARGS__ vlib_buffer_callbacks_t __##x##_buffer_callbacks; \ static void __vlib_add_buffer_callbacks_t_##x (void) \ __attribute__((__constructor__)) ; \ static void __vlib_add_buffer_callbacks_t_##x (void) \ { \ if (vlib_buffer_callbacks) \ clib_panic ("vlib buffer callbacks already registered"); \ vlib_buffer_callbacks = &__##x##_buffer_callbacks; \ } \ __VA_ARGS__ vlib_buffer_callbacks_t __##x##_buffer_callbacks /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */