| /* -*- 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); |
| } |