blob: 45a32340e1575842563638665766d8acdf9993dd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <qpid/dispatch/iterator.h>
#include <qpid/dispatch/ctools.h>
#include "alloc.h"
#include <qpid/dispatch/log.h>
#include "message_private.h"
#include <stdio.h>
#include <string.h>
//static const char *log_module = "FIELD";
typedef enum {
MODE_TO_END,
MODE_TO_SLASH
} parse_mode_t;
typedef enum {
STATE_AT_PREFIX,
STATE_AT_PHASE,
STATE_IN_ADDRESS
} addr_state_t;
typedef struct {
qd_buffer_t *buffer;
unsigned char *cursor;
int length;
} pointer_t;
typedef struct qd_hash_segment_t qd_hash_segment_t;
struct qd_hash_segment_t {
DEQ_LINKS(qd_hash_segment_t); //Adds the *prev and *next links
uint32_t hash; //The hash of the segment
uint32_t segment_length; //The length of each hash segment
};
DEQ_DECLARE(qd_hash_segment_t, qd_hash_segment_list_t);
ALLOC_DECLARE(qd_hash_segment_t);
ALLOC_DEFINE(qd_hash_segment_t);
struct qd_field_iterator_t {
pointer_t start_pointer;
pointer_t view_start_pointer;
pointer_t pointer;
qd_iterator_view_t view;
qd_hash_segment_list_t hash_segments;
parse_mode_t mode;
addr_state_t state;
bool view_prefix;
unsigned char prefix;
unsigned char prefix_override;
unsigned char phase;
};
ALLOC_DECLARE(qd_field_iterator_t);
ALLOC_DEFINE(qd_field_iterator_t);
typedef enum {
STATE_START,
STATE_SLASH_LEFT,
STATE_SKIPPING_TO_NEXT_SLASH,
STATE_SCANNING,
STATE_COLON,
STATE_COLON_SLASH,
STATE_AT_NODE_ID
} state_t;
static char *my_area = "";
static char *my_router = "";
const char SEPARATOR = '.';
const uint32_t HASH_INIT = 5381;
static void parse_address_view(qd_field_iterator_t *iter)
{
//
// This function starts with an iterator view that is identical to
// ITER_VIEW_NO_HOST. We will now further refine the view in order
// to aid the router in looking up addresses.
//
if (iter->prefix_override == '\0' && qd_field_iterator_prefix(iter, "_")) {
if (qd_field_iterator_prefix(iter, "local/")) {
iter->prefix = 'L';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
return;
}
if (qd_field_iterator_prefix(iter, "topo/")) {
if (qd_field_iterator_prefix(iter, "all/") || qd_field_iterator_prefix(iter, my_area)) {
if (qd_field_iterator_prefix(iter, "all/")) {
iter->prefix = 'T';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
return;
} else if (qd_field_iterator_prefix(iter, my_router)) {
iter->prefix = 'L';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
return;
}
iter->prefix = 'R';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
iter->mode = MODE_TO_SLASH;
return;
}
iter->prefix = 'A';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
iter->mode = MODE_TO_SLASH;
return;
}
}
iter->prefix = iter->prefix_override ? iter->prefix_override : 'M';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
}
static void parse_node_view(qd_field_iterator_t *iter)
{
//
// This function starts with an iterator view that is identical to
// ITER_VIEW_NO_HOST. We will now further refine the view in order
// to aid the router in looking up nodes.
//
if (qd_field_iterator_prefix(iter, my_area)) {
iter->prefix = 'R';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
iter->mode = MODE_TO_END;
return;
}
iter->prefix = 'A';
iter->state = STATE_AT_PREFIX;
iter->view_prefix = true;
iter->mode = MODE_TO_SLASH;
}
static void qd_address_iterator_check_trailing_octet(qd_field_iterator_t *iter, char octet)
{
// Save the iterator's pointer so we can apply it back before returning from this function.
pointer_t save_pointer = iter->pointer;
char current_octet = 0;
while(!qd_field_iterator_end(iter)) {
current_octet = qd_field_iterator_octet(iter);
}
// We have the last octet in current_octet
if (current_octet == octet) {
iter->pointer = save_pointer;
iter->pointer.length-- ;
}
else {
iter->pointer = save_pointer;
}
}
static void view_initialize(qd_field_iterator_t *iter)
{
//
// The default behavior is for the view to *not* have a prefix.
// We'll add one if it's needed later.
//
iter->state = STATE_IN_ADDRESS;
iter->view_prefix = false;
iter->mode = MODE_TO_END;
if (iter->view == ITER_VIEW_ALL)
return;
//
// Advance to the node-id.
//
state_t state = STATE_START;
unsigned int octet;
pointer_t save_pointer = {0,0,0};
while (!qd_field_iterator_end(iter) && state != STATE_AT_NODE_ID) {
octet = qd_field_iterator_octet(iter);
switch (state) {
case STATE_START :
if (octet == '/') {
state = STATE_SLASH_LEFT;
save_pointer = iter->pointer;
} else
state = STATE_SCANNING;
break;
case STATE_SLASH_LEFT :
if (octet == '/')
state = STATE_SKIPPING_TO_NEXT_SLASH;
else {
state = STATE_AT_NODE_ID;
iter->pointer = save_pointer;
}
break;
case STATE_SKIPPING_TO_NEXT_SLASH :
if (octet == '/')
state = STATE_AT_NODE_ID;
break;
case STATE_SCANNING :
if (octet == ':')
state = STATE_COLON;
break;
case STATE_COLON :
if (octet == '/') {
state = STATE_COLON_SLASH;
save_pointer = iter->pointer;
} else
state = STATE_SCANNING;
break;
case STATE_COLON_SLASH :
if (octet == '/')
state = STATE_SKIPPING_TO_NEXT_SLASH;
else {
state = STATE_AT_NODE_ID;
iter->pointer = save_pointer;
}
break;
case STATE_AT_NODE_ID :
break;
}
}
if (state != STATE_AT_NODE_ID) {
//
// The address string was relative, not absolute. The node-id
// is at the beginning of the string.
//
iter->pointer = iter->start_pointer;
}
//
// Cursor is now on the first octet of the node-id
//
if (iter->view == ITER_VIEW_NODE_ID) {
iter->mode = MODE_TO_SLASH;
return;
}
if (iter->view == ITER_VIEW_NO_HOST) {
iter->mode = MODE_TO_END;
return;
}
if (iter->view == ITER_VIEW_ADDRESS_HASH) {
iter->mode = MODE_TO_END;
qd_address_iterator_check_trailing_octet(iter, SEPARATOR);
parse_address_view(iter);
return;
}
if (iter->view == ITER_VIEW_NODE_HASH) {
iter->mode = MODE_TO_END;
parse_node_view(iter);
return;
}
if (iter->view == ITER_VIEW_NODE_SPECIFIC) {
iter->mode = MODE_TO_END;
while (!qd_field_iterator_end(iter)) {
octet = qd_field_iterator_octet(iter);
if (octet == '/')
break;
}
return;
}
}
static inline void field_iterator_move_cursor(qd_field_iterator_t *iter, uint32_t length)
{
// Only safe to call this help method if the cursor is parsing the data,
// i.e. if iter is an address iterator, the cursor must be 'past' the
// prefix
assert(iter->state == STATE_IN_ADDRESS);
uint32_t count = ((length > iter->pointer.length)
? iter->pointer.length
: length);
if (iter->pointer.buffer) {
while (count) {
uint32_t remaining = qd_buffer_cursor(iter->pointer.buffer) - iter->pointer.cursor;
remaining = (remaining > count) ? count : remaining;
iter->pointer.cursor += remaining;
iter->pointer.length -= remaining;
count -= remaining;
if (iter->pointer.cursor == qd_buffer_cursor(iter->pointer.buffer)) {
iter->pointer.buffer = iter->pointer.buffer->next;
if (iter->pointer.buffer == 0) {
iter->pointer.length = 0;
iter->pointer.cursor = 0;
break;
} else {
iter->pointer.cursor = qd_buffer_base(iter->pointer.buffer);
}
}
}
} else { // string/binary data
iter->pointer.cursor += count;
iter->pointer.length -= count;
}
}
void qd_field_iterator_set_address(const char *area, const char *router)
{
my_area = (char*) malloc(strlen(area) + 2);
strcpy(my_area, area);
strcat(my_area, "/");
my_router = (char*) malloc(strlen(router) + 2);
strcpy(my_router, router);
strcat(my_router, "/");
}
qd_field_iterator_t* qd_address_iterator_string(const char *text, qd_iterator_view_t view)
{
qd_field_iterator_t *iter = new_qd_field_iterator_t();
if (!iter)
return 0;
iter->start_pointer.buffer = 0;
iter->start_pointer.cursor = (unsigned char*) text;
iter->start_pointer.length = strlen(text);
iter->phase = '0';
iter->prefix_override = '\0';
DEQ_INIT(iter->hash_segments);
qd_address_iterator_reset_view(iter, view);
return iter;
}
qd_field_iterator_t* qd_address_iterator_binary(const char *text, int length, qd_iterator_view_t view)
{
qd_field_iterator_t *iter = new_qd_field_iterator_t();
if (!iter)
return 0;
iter->start_pointer.buffer = 0;
iter->start_pointer.cursor = (unsigned char*) text;
iter->start_pointer.length = length;
iter->phase = '0';
iter->prefix_override = '\0';
DEQ_INIT(iter->hash_segments);
qd_address_iterator_reset_view(iter, view);
return iter;
}
qd_field_iterator_t *qd_address_iterator_buffer(qd_buffer_t *buffer, int offset, int length, qd_iterator_view_t view)
{
qd_field_iterator_t *iter = new_qd_field_iterator_t();
if (!iter)
return 0;
iter->start_pointer.buffer = buffer;
iter->start_pointer.cursor = qd_buffer_base(buffer) + offset;
iter->start_pointer.length = length;
iter->phase = '0';
iter->prefix_override = '\0';
DEQ_INIT(iter->hash_segments);
qd_address_iterator_reset_view(iter, view);
return iter;
}
void qd_field_iterator_free(qd_field_iterator_t *iter)
{
if (!iter) return;
free_qd_field_iterator_t(iter);
}
void qd_field_iterator_reset(qd_field_iterator_t *iter)
{
iter->pointer = iter->view_start_pointer;
iter->state = iter->view_prefix ? STATE_AT_PREFIX : STATE_IN_ADDRESS;
}
void qd_address_iterator_reset_view(qd_field_iterator_t *iter, qd_iterator_view_t view)
{
iter->pointer = iter->start_pointer;
iter->view = view;
view_initialize(iter);
iter->view_start_pointer = iter->pointer;
}
qd_iterator_view_t qd_address_iterator_get_view(const qd_field_iterator_t *iter)
{
return iter->view;
}
void qd_address_iterator_set_phase(qd_field_iterator_t *iter, char phase)
{
iter->phase = phase;
}
void qd_address_iterator_override_prefix(qd_field_iterator_t *iter, char prefix)
{
iter->prefix_override = prefix;
qd_address_iterator_reset_view(iter, iter->view);
}
unsigned char qd_field_iterator_octet(qd_field_iterator_t *iter)
{
if (iter->state == STATE_AT_PREFIX) {
iter->state = iter->prefix == 'M' ? STATE_AT_PHASE : STATE_IN_ADDRESS;
return iter->prefix;
}
if (iter->state == STATE_AT_PHASE) {
iter->state = STATE_IN_ADDRESS;
return iter->phase;
}
if (iter->pointer.length == 0)
return (unsigned char) 0;
unsigned char result = *(iter->pointer.cursor);
field_iterator_move_cursor(iter, 1);
if (iter->pointer.length && iter->mode == MODE_TO_SLASH && *(iter->pointer.cursor) == '/')
iter->pointer.length = 0;
return result;
}
int qd_field_iterator_end(const qd_field_iterator_t *iter)
{
return iter->pointer.length == 0;
}
qd_field_iterator_t *qd_field_iterator_sub(const qd_field_iterator_t *iter, uint32_t length)
{
qd_field_iterator_t *sub = new_qd_field_iterator_t();
if (!sub)
return 0;
sub->start_pointer = iter->pointer;
sub->start_pointer.length = length;
sub->view_start_pointer = sub->start_pointer;
sub->pointer = sub->start_pointer;
sub->view = iter->view;
sub->mode = iter->mode;
sub->state = STATE_IN_ADDRESS;
sub->view_prefix = false;
sub->prefix_override = '\0';
sub->phase = '0';
DEQ_INIT(sub->hash_segments);
return sub;
}
void qd_field_iterator_advance(qd_field_iterator_t *iter, uint32_t length)
{
while (length > 0 && !qd_field_iterator_end(iter)) {
if (iter->state == STATE_IN_ADDRESS) {
field_iterator_move_cursor(iter, length);
break;
} else {
qd_field_iterator_octet(iter);
length--;
}
}
}
uint32_t qd_field_iterator_remaining(const qd_field_iterator_t *iter)
{
return iter->pointer.length;
}
int qd_field_iterator_equal(qd_field_iterator_t *iter, const unsigned char *string)
{
qd_field_iterator_reset(iter);
while (!qd_field_iterator_end(iter) && *string) {
if (*string != qd_field_iterator_octet(iter))
break;
string++;
}
int match = (qd_field_iterator_end(iter) && (*string == 0));
qd_field_iterator_reset(iter);
return match;
}
int qd_field_iterator_prefix(qd_field_iterator_t *iter, const char *prefix)
{
pointer_t save_pointer = iter->pointer;
unsigned char *c = (unsigned char*) prefix;
while(*c) {
if (*c != qd_field_iterator_octet(iter))
break;
c++;
}
if (*c) {
iter->pointer = save_pointer;
return 0;
}
return 1;
}
int qd_field_iterator_length(const qd_field_iterator_t *iter)
{
qd_field_iterator_t copy = *iter;
int length = 0;
qd_field_iterator_reset(&copy);
while (!qd_field_iterator_end(&copy)) {
qd_field_iterator_octet(&copy);
length++;
}
return length;
}
int qd_field_iterator_ncopy(qd_field_iterator_t *iter, unsigned char* buffer, int n) {
qd_field_iterator_reset(iter);
int i = 0;
while (!qd_field_iterator_end(iter) && i < n)
buffer[i++] = qd_field_iterator_octet(iter);
return i;
}
char* qd_field_iterator_strncpy(qd_field_iterator_t *iter, char* buffer, int n) {
int i = qd_field_iterator_ncopy(iter, (unsigned char*)buffer, n-1);
buffer[i] = '\0';
return buffer;
}
unsigned char *qd_field_iterator_copy(qd_field_iterator_t *iter)
{
int length = qd_field_iterator_length(iter);
unsigned char *copy = malloc(length+1);
int i = qd_field_iterator_ncopy(iter, copy, length+1);
copy[i] = '\0';
return copy;
}
qd_field_iterator_t *qd_field_iterator_dup(const qd_field_iterator_t *iter)
{
if (iter == 0)
return 0;
qd_field_iterator_t *dup = new_qd_field_iterator_t();
if (dup)
*dup = *iter;
return dup;
}
qd_iovec_t *qd_field_iterator_iovec(const qd_field_iterator_t *iter)
{
assert(!iter->view_prefix); // Not supported for views with a prefix
//
// Count the number of buffers this field straddles
//
pointer_t pointer = iter->view_start_pointer;
int bufcnt = 1;
qd_buffer_t *buf = pointer.buffer;
size_t bufsize = qd_buffer_size(buf) - (pointer.cursor - qd_buffer_base(pointer.buffer));
ssize_t remaining = pointer.length - bufsize;
while (remaining > 0) {
bufcnt++;
buf = buf->next;
if (!buf)
return 0;
remaining -= qd_buffer_size(buf);
}
//
// Allocate an iovec object big enough to hold the number of buffers
//
qd_iovec_t *iov = qd_iovec(bufcnt);
if (!iov)
return 0;
//
// Build out the io vectors with pointers to the segments of the field in buffers
//
bufcnt = 0;
buf = pointer.buffer;
bufsize = qd_buffer_size(buf) - (pointer.cursor - qd_buffer_base(pointer.buffer));
void *base = pointer.cursor;
remaining = pointer.length;
while (remaining > 0) {
if (bufsize > remaining)
bufsize = remaining;
qd_iovec_array(iov)[bufcnt].iov_base = base;
qd_iovec_array(iov)[bufcnt].iov_len = bufsize;
bufcnt++;
remaining -= bufsize;
if (remaining > 0) {
buf = buf->next;
base = qd_buffer_base(buf);
bufsize = qd_buffer_size(buf);
}
}
return iov;
}
uint32_t qd_iterator_hash_function(qd_field_iterator_t *iter)
{
uint32_t hash = HASH_INIT;
qd_field_iterator_reset(iter);
while (!qd_field_iterator_end(iter))
hash = ((hash << 5) + hash) + (int) qd_field_iterator_octet(iter); /* hash * 33 + c */
return hash;
}
/**
* Creates and returns a new qd_hash_segment_t and initializes it.
*/
static qd_hash_segment_t *qd_iterator_hash_segment(void)
{
qd_hash_segment_t *hash_segment = new_qd_hash_segment_t();
DEQ_ITEM_INIT(hash_segment);
hash_segment->hash = 0;
hash_segment->segment_length = 0;
return hash_segment;
}
/**
* Create a new hash segment and insert it at the end of the linked list
*/
static void qd_insert_hash_segment(qd_field_iterator_t *iter, uint32_t *hash, int segment_length)
{
qd_hash_segment_t *hash_segment = qd_iterator_hash_segment();
// While storing the segment, don't include the hash of the separator in the segment but do include it in the overall hash.
hash_segment->hash = *hash;
hash_segment->segment_length = segment_length;
DEQ_INSERT_TAIL(iter->hash_segments, hash_segment);
}
void qd_iterator_hash_segments(qd_field_iterator_t *iter)
{
// Reset the pointers in the iterator
qd_field_iterator_reset(iter);
uint32_t hash = HASH_INIT;
char octet;
int segment_length=0;
while (!qd_field_iterator_end(iter)) {
// Get the octet at which the iterator is currently pointing to.
octet = qd_field_iterator_octet(iter);
segment_length += 1;
if (octet == SEPARATOR) {
qd_insert_hash_segment(iter, &hash, segment_length-1);
}
hash = ((hash << 5) + hash) + octet; /* hash * 33 + c */
}
// Segments should never end with a separator. see view_initialize which in turn calls qd_address_iterator_check_trailing_octet
// Insert the last segment which was not inserted in the previous while loop
qd_insert_hash_segment(iter, &hash, segment_length);
// Return the pointers in the iterator back to the original state before returning from this function.
qd_field_iterator_reset(iter);
}
bool qd_iterator_hash_and_reset(qd_field_iterator_t *iter, uint32_t *hash)
{
qd_hash_segment_t *hash_segment = DEQ_TAIL(iter->hash_segments);
if (!hash_segment)
return false;
*hash = hash_segment->hash;
// Get the length of the hashed segment and set it on the iterator so that the iterator can only advance till that length
// Check for a non empty iter->prefix and reduce the segment length by 1
if (iter->view_prefix) {
if (iter->prefix == 'M')
iter->view_start_pointer.length = hash_segment->segment_length - 2;
else
iter->view_start_pointer.length = hash_segment->segment_length - 1;
}
// Remove the tail from the hash segments since we have already compared it.
DEQ_REMOVE_TAIL(iter->hash_segments);
free_qd_hash_segment_t(hash_segment);
return true;
}