diff --git a/scripts/base/init-default.bro b/scripts/base/init-default.bro index 8b36899f10..563f8af0bc 100644 --- a/scripts/base/init-default.bro +++ b/scripts/base/init-default.bro @@ -12,6 +12,7 @@ @load base/utils/numbers @load base/utils/paths @load base/utils/patterns +@load base/utils/queue @load base/utils/strings @load base/utils/thresholds @load base/utils/urls diff --git a/scripts/base/utils/queue.bro b/scripts/base/utils/queue.bro new file mode 100644 index 0000000000..c5e3bcf906 --- /dev/null +++ b/scripts/base/utils/queue.bro @@ -0,0 +1,177 @@ +##! A FIFO string queue. + +module Queue; + +export { + ## Settings for initializing the queue. + type Settings: record { + ## If a maximum length is set for the queue + ## it will maintain itself at that + ## maximum length automatically. + max_len: count &optional; + }; + + ## The internal data structure for the queue. + type Queue: record {}; + + ## Initialize a queue record structure. + ## + ## s: A :bro:record:`Settings` record configuring the queue. + ## + ## Returns: An opaque queue record. + global init: function(s: Settings): Queue; + + ## Push a string onto the top of a queue. + ## + ## q: The queue to push the string into. + ## + ## val: The string to push + global push: function(q: Queue, val: any); + + ## Pop a string from the bottom of a queue. + ## + ## q: The queue to pop the string from. + ## + ## Returns: The string popped from the queue. + global pop: function(q: Queue): any; + + ## Merge two queue's together. If any settings are applied + ## to the queues, the settings from q1 are used for the new + ## merged queue. + ## + ## q1: The first queue. Settings are taken from here. + ## + ## q2: The second queue. + ## + ## Returns: A new queue from merging the other two together. + global merge: function(q1: Queue, q2: Queue): Queue; + + ## Get the number of items in a queue. + ## + ## q: The queue. + ## + ## Returns: The length of the queue. + global len: function(q: Queue): count; + + ## Get the contents of the queue as a string vector. + ## + ## q: The queue. + ## + ## Returns: A :bro:type:`vector of string` containing the + ## current contents of q. + global get_str_vector: function(q: Queue): vector of string; + + ## Get the contents of the queue as a count vector. Use care + ## with this function. If the data put into the queue wasn't + ## integers you will get conversion errors. + ## + ## q: The queue. + ## + ## Returns: A :bro:type:`vector of count` containing the + ## current contents of q. + global get_cnt_vector: function(q: Queue): vector of count; +} + +redef record Queue += { + # Indicator for if the queue was appropriately initialized. + initialized: bool &default=F; + # The values are stored here. + vals: table[count] of any &optional; + # Settings for the queue. + settings: Settings &optional; + # The top value in the vals table. + top: count &default=0; + # The bottom value in the vals table. + bottom: count &default=0; + # The number of bytes in the queue. + size: count &default=0; +}; + +function init(s: Settings): Queue + { + local q: Queue; + q$vals=table(); + q$settings = copy(s); + q$initialized=T; + return q; + } + +function push(q: Queue, val: any) + { + if ( q$settings?$max_len && len(q) >= q$settings$max_len ) + pop(q); + q$vals[q$top] = val; + ++q$top; + } + +function pop(q: Queue): any + { + local ret = q$vals[q$bottom]; + delete q$vals[q$bottom]; + ++q$bottom; + return ret; + } + +function merge(q1: Queue, q2: Queue): Queue + { + local ret = init(q1$settings); + local i = q1$bottom; + local j = q2$bottom; + for ( ignored_val in q1$vals ) + { + if ( i in q1$vals ) + push(ret, q1$vals[i]); + if ( j in q2$vals ) + push(ret, q2$vals[j]); + ++i; + ++j; + } + } + +function len(q: Queue): count + { + return |q$vals|; + } + +function get_str_vector(q: Queue): vector of string + { + local ret: vector of string; + local i = q$bottom; + local j = 0; + # Really dumb hack, this is only to provide + # the iteration for the correct number of + # values in q$vals. + for ( ignored_val in q$vals ) + { + if ( i >= q$top ) + break; + + ret[j] = cat(q$vals[i]); + ++j; ++i; + } + return ret; + } + +function get_cnt_vector(q: Queue): vector of count + { + local ret: vector of count; + local i = q$bottom; + local j = 0; + # Really dumb hack, this is only to provide + # the iteration for the correct number of + # values in q$vals. + for ( ignored_val in q$vals ) + { + if ( i >= q$top ) + break; + + # TODO: this is terrible and should be replaced by + # a more generic version of the various + # functions to get vectors of values. + # (the way "any" works right now makes this impossible though) + ret[j] = to_count(cat(q$vals[i])); + ++j; ++i; + } + return ret; + } + diff --git a/testing/btest/Baseline/scripts.base.utils.queue/output b/testing/btest/Baseline/scripts.base.utils.queue/output new file mode 100644 index 0000000000..b878006310 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.utils.queue/output @@ -0,0 +1,11 @@ +This is a get_cnt_vector test: 3 +This is a get_cnt_vector test: 4 +This is a get_str_vector test: 3 +This is a get_str_vector test: 4 +Testing pop: 3 +Length after pop: 1 +Size of q2: 4 +String queue value: test 1 +String queue value: test 2 +String queue value: test 2 +String queue value: test 1 diff --git a/testing/btest/scripts/base/utils/queue.test b/testing/btest/scripts/base/utils/queue.test new file mode 100644 index 0000000000..50f541a25f --- /dev/null +++ b/testing/btest/scripts/base/utils/queue.test @@ -0,0 +1,35 @@ +# @TEST-EXEC: bro -b %INPUT > output +# @TEST-EXEC: btest-diff output + +# This is loaded by default +@load base/utils/queue + +event bro_init() + { + local q = Queue::init([$max_len=2]); + Queue::push(q, 1); + Queue::push(q, 2); + Queue::push(q, 3); + Queue::push(q, 4); + local test1 = Queue::get_cnt_vector(q); + for ( i in test1 ) + print fmt("This is a get_cnt_vector test: %d", test1[i]); + + local test2 = Queue::get_str_vector(q); + for ( i in test2 ) + print fmt("This is a get_str_vector test: %s", test2[i]); + + local test_val = Queue::pop(q); + print fmt("Testing pop: %s", test_val); + print fmt("Length after pop: %d", Queue::len(q)); + + local q2 = Queue::init([]); + Queue::push(q2, "test 1"); + Queue::push(q2, "test 2"); + Queue::push(q2, "test 2"); + Queue::push(q2, "test 1"); + print fmt("Size of q2: %d", Queue::len(q2)); + local test3: vector of string = Queue::get_str_vector(q2); + for ( i in test3 ) + print fmt("String queue value: %s", test3[i]); + } \ No newline at end of file