blob: e7a54e711a35049354f2328a0e148df6531d2c7f [file] [log] [blame]
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 University of California, Los Angeles
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Ilya Moiseenko <iliamo@cs.ucla.edu>
*/
#include "ccn_coding.h"
/**
* @file ccn_coding.c
* @brief Support for scanning and parsing ccnb-encoded data.
*
* Part of the CCNx C Library.
*
* Copyright (C) 2008, 2009 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
//#include <ccn/coding.h>
/**
* This macro documents what's happening in the state machine by
* hinting at the XML syntax would be emitted in a re-encoder.
* But it actually does nothing.
*/
#define XML(goop) ((void)0)
/**
* Decodes ccnb decoded data
*
* @param d holds the current state of the decoder.
* @param p points to a new block of ccnb data to feed to the decoder.
* @param n is the size of the input, in bytes.
* @returns the number of bytes consumed.
*
* The client should ensure that the decoder is initialized to all zero
* before the first call. In the default mode, the decoder will return
* only when it runs out of data, encounters an error, or reaches the end
* of the element that it started at. This is a good way to pull
* ccnb-encoded objects from a byte stream.
*
* By setting the CCN_DSTATE_PAUSE bit is set in the decoder state, the
* decoder will additionally return just after recognizing each token.
* In this instance, use CCN_GET_TT_FROM_DSTATE() to extract
* the token type from the decoder state;
* CCN_CLOSE will be reported as CCN_NO_TOKEN.
*
* The pause bit persists, so the end test should take that into account
* by using the CCN_FINAL_DSTATE() macro instead of testing for state 0.
*
* Once an error state is entered, no addition input is processed.
*
* @see ccn_buf_decoder_start(), ccn_buf_advance(), ccn_buf_check_close()
*/
ssize_t
ccn_skeleton_decode(struct ccn_skeleton_decoder *d,
const unsigned char *p, size_t n)
{
enum ccn_decoder_state state = (ccn_decoder_state)(d->state);
int tagstate = 0;
size_t numval = d->numval;
ssize_t i = 0;
unsigned char c;
size_t chunk;
int pause = 0;
if (d->state >= 0) {
pause = d->state & CCN_DSTATE_PAUSE;
tagstate = (d->state >> 8) & 3;
state = (ccn_decoder_state)(d->state & 0xFF);
}
while (i < (ssize_t)n) {
switch (state) {
case CCN_DSTATE_INITIAL:
case CCN_DSTATE_NEWTOKEN: /* start new thing */
d->token_index = i + d->index;
if (tagstate > 1 && tagstate-- == 2) {
XML("\""); /* close off the attribute value */
}
if (p[i] == CCN_CLOSE) {
i++;
if (d->nest <= 0 || tagstate > 1) {
state = CCN_DSTATE_ERR_NEST;
break;
}
if (tagstate == 1) {
tagstate = 0;
XML("/>");
}
else {
XML("</%s>");
}
d->nest -= 1;
if (d->nest == 0) {
state = CCN_DSTATE_INITIAL;
n = i;
}
if (pause) {
int temp = (int)state;
//state |= (((int)CCN_NO_TOKEN) << 16);
temp |= (((int)CCN_NO_TOKEN) << 16);
state = (ccn_decoder_state)temp;
n = i;
}
break;
}
numval = 0;
state = CCN_DSTATE_NUMVAL;
/* FALLTHRU */
case CCN_DSTATE_NUMVAL: /* parsing numval */
c = p[i++];
if ((c & CCN_TT_HBIT) == CCN_CLOSE) {
if (numval > ((~(size_t)0U) >> (7 + CCN_TT_BITS)))
state = CCN_DSTATE_ERR_OVERFLOW;
numval = (numval << 7) + (c & 127);
}
else {
numval = (numval << (7-CCN_TT_BITS)) +
((c >> CCN_TT_BITS) & CCN_MAX_TINY);
c &= CCN_TT_MASK;
switch (c) {
case CCN_EXT:
if (tagstate == 1) {
tagstate = 0;
XML(">");
}
d->nest += 1;
d->element_index = d->token_index;
state = CCN_DSTATE_NEWTOKEN;
break;
case CCN_DTAG:
if (tagstate == 1) {
tagstate = 0;
XML(">");
}
d->nest += 1;
d->element_index = d->token_index;
XML("<%s");
tagstate = 1;
state = CCN_DSTATE_NEWTOKEN;
break;
case CCN_BLOB:
if (tagstate == 1) {
tagstate = 0;
XML(" ccnbencoding=\"base64Binary\">");
}
state = CCN_DSTATE_BLOB;
if (numval == 0)
state = CCN_DSTATE_NEWTOKEN;
break;
case CCN_UDATA:
if (tagstate == 1) {
tagstate = 0;
XML(">");
}
state = CCN_DSTATE_UDATA;
if (numval == 0)
state = CCN_DSTATE_NEWTOKEN;
break;
case CCN_DATTR:
if (tagstate != 1) {
state = CCN_DSTATE_ERR_ATTR;
break;
}
tagstate = 3;
state = CCN_DSTATE_NEWTOKEN;
break;
case CCN_ATTR:
if (tagstate != 1) {
state = CCN_DSTATE_ERR_ATTR;
break;
}
numval += 1; /* encoded as length-1 */
state = CCN_DSTATE_ATTRNAME;
break;
case CCN_TAG:
if (tagstate == 1) {
tagstate = 0;
XML(">");
}
numval += 1; /* encoded as length-1 */
d->nest += 1;
d->element_index = d->token_index;
state = CCN_DSTATE_TAGNAME;
break;
default:
state = CCN_DSTATE_ERR_CODING;
}
if (pause) {
int temp = (int)state;
//state |= (c << 16);
temp |= (c << 16);
state = (ccn_decoder_state)temp;
n = i;
}
}
break;
case CCN_DSTATE_TAGNAME: /* parsing tag name */
chunk = n - i;
if (chunk > numval)
chunk = numval;
if (chunk == 0) {
state = CCN_DSTATE_ERR_BUG;
break;
}
numval -= chunk;
i += chunk;
if (numval == 0) {
if (d->nest == 0) {
state = CCN_DSTATE_ERR_NEST;
break;
}
XML("<%s");
tagstate = 1;
state = CCN_DSTATE_NEWTOKEN;
}
break;
case CCN_DSTATE_ATTRNAME: /* parsing attribute name */
chunk = n - i;
if (chunk > numval)
chunk = numval;
if (chunk == 0) {
state = CCN_DSTATE_ERR_BUG;
break;
}
numval -= chunk;
i += chunk;
if (numval == 0) {
if (d->nest == 0) {
state = CCN_DSTATE_ERR_ATTR;
break;
}
XML(" %s=\"");
tagstate = 3;
state = CCN_DSTATE_NEWTOKEN;
}
break;
case CCN_DSTATE_UDATA: /* utf-8 data */
case CCN_DSTATE_BLOB: /* BLOB */
chunk = n - i;
if (chunk > numval)
chunk = numval;
if (chunk == 0) {
state = CCN_DSTATE_ERR_BUG;
break;
}
numval -= chunk;
i += chunk;
if (numval == 0)
state = CCN_DSTATE_NEWTOKEN;
break;
default:
n = i;
}
}
if (state < 0)
tagstate = pause = 0;
d->state = state | pause | (tagstate << 8);
d->numval = numval;
d->index += i;
return(i);
}