| /* skel-test.c --- tests for the skeleton functions |
| * |
| * ==================================================================== |
| * Copyright (c) 2000-2002 CollabNet. All rights reserved. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at http://subversion.tigris.org/license-1.html. |
| * If newer versions of this license are posted there, you may use a |
| * newer version instead, at your option. |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals. For exact contribution history, see the revision |
| * history and logs, available at http://subversion.tigris.org/. |
| * ==================================================================== |
| */ |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include "apr.h" |
| #include "svn_pools.h" |
| #include "svn_string.h" |
| #include "../../libsvn_fs/fs.h" |
| #include "../../libsvn_fs/skel.h" |
| |
| |
| /* Some utility functions. */ |
| |
| |
| /* A quick way to create error messages. */ |
| static svn_error_t * |
| fail (apr_pool_t *pool, const char *fmt, ...) |
| { |
| va_list ap; |
| char *msg; |
| |
| va_start (ap, fmt); |
| msg = apr_pvsprintf (pool, fmt, ap); |
| va_end (ap); |
| |
| return svn_error_create (SVN_ERR_TEST_FAILED, 0, 0, pool, msg); |
| } |
| |
| |
| /* Free everything from pool, and return an empty Subversion string. */ |
| static svn_stringbuf_t * |
| get_empty_string (apr_pool_t *pool) |
| { |
| svn_pool_clear (pool); |
| |
| return svn_stringbuf_ncreate (0, 0, pool); |
| } |
| |
| /* Parse a skeleton from a Subversion string. */ |
| static skel_t * |
| parse_str (svn_stringbuf_t *str, apr_pool_t *pool) |
| { |
| return svn_fs__parse_skel (str->data, str->len, pool); |
| } |
| |
| |
| /* Parse a skeleton from a C string. */ |
| static skel_t * |
| parse_cstr (char *str, apr_pool_t *pool) |
| { |
| return svn_fs__parse_skel (str, strlen (str), pool); |
| } |
| |
| |
| enum char_type { |
| type_nothing = 0, |
| type_space = 1, |
| type_digit = 2, |
| type_paren = 3, |
| type_name = 4, |
| }; |
| |
| static int skel_char_map_initialized; |
| static enum char_type skel_char_map[256]; |
| |
| static void |
| init_char_types (void) |
| { |
| int i; |
| const char *c; |
| |
| if (skel_char_map_initialized) |
| return; |
| |
| for (i = 0; i < 256; i++) |
| skel_char_map[i] = type_nothing; |
| |
| for (i = '0'; i <= '9'; i++) |
| skel_char_map[i] = type_digit; |
| |
| for (c = "\t\n\f\r "; *c; c++) |
| skel_char_map[(unsigned char) *c] = type_space; |
| |
| for (c = "()[]"; *c; c++) |
| skel_char_map[(unsigned char) *c] = type_paren; |
| |
| for (i = 'A'; i <= 'Z'; i++) |
| skel_char_map[i] = type_name; |
| for (i = 'a'; i <= 'z'; i++) |
| skel_char_map[i] = type_name; |
| |
| skel_char_map_initialized = 1; |
| } |
| |
| /* Return true iff BYTE is a whitespace byte. */ |
| static int |
| skel_is_space (char byte) |
| { |
| init_char_types (); |
| |
| return skel_char_map[(unsigned char) byte] == type_space; |
| } |
| |
| #if 0 |
| /* Return true iff BYTE is a digit byte. */ |
| static int |
| skel_is_digit (char byte) |
| { |
| init_char_types (); |
| |
| return skel_char_map[(unsigned char) byte] == type_digit; |
| } |
| #endif |
| |
| /* Return true iff BYTE is a paren byte. */ |
| static int |
| skel_is_paren (char byte) |
| { |
| init_char_types (); |
| |
| return skel_char_map[(unsigned char) byte] == type_paren; |
| } |
| |
| /* Return true iff BYTE is a name byte. */ |
| static int |
| skel_is_name (char byte) |
| { |
| init_char_types (); |
| |
| return skel_char_map[(unsigned char) byte] == type_name; |
| } |
| |
| |
| /* Check that SKEL is an atom, and its contents match LEN bytes of |
| DATA. */ |
| static int |
| check_atom (skel_t *skel, const char *data, int len) |
| { |
| return (skel |
| && skel->is_atom |
| && skel->len == len |
| && ! memcmp (skel->data, data, len)); |
| } |
| |
| |
| /* Functions that generate/check interesting implicit-length atoms. */ |
| |
| |
| /* Append to STR an implicit-length atom consisting of the byte BYTE, |
| terminated by the character TERM. BYTE must be a name byte, |
| and TERM must be a valid skel separator, or NUL. */ |
| static void |
| put_implicit_length_byte (svn_stringbuf_t *str, char byte, char term) |
| { |
| if (! skel_is_name (byte)) |
| abort (); |
| if (term != '\0' |
| && ! skel_is_space (term) |
| && ! skel_is_paren (term)) |
| abort (); |
| svn_stringbuf_appendbytes (str, &byte, 1); |
| if (term != '\0') |
| svn_stringbuf_appendbytes (str, &term, 1); |
| } |
| |
| |
| /* Return true iff SKEL is the parsed form of the atom produced by |
| calling put_implicit_length with BYTE. */ |
| static int |
| check_implicit_length_byte (skel_t *skel, char byte) |
| { |
| if (! skel_is_name (byte)) |
| abort (); |
| |
| return check_atom (skel, &byte, 1); |
| } |
| |
| |
| /* Subroutine for the *_implicit_length_all_chars functions. */ |
| static char * |
| gen_implicit_length_all_chars (int *len_p) |
| { |
| int pos; |
| int i; |
| static char name[256]; |
| |
| /* Gotta start with a valid name character. */ |
| pos = 0; |
| name[pos++] = 'x'; |
| for (i = 0; i < 256; i++) |
| if (! skel_is_space ( (apr_byte_t)i) |
| && ! skel_is_paren ( (apr_byte_t)i)) |
| name[pos++] = i; |
| |
| *len_p = pos; |
| return name; |
| } |
| |
| |
| /* Append to STR an implicit-length atom containing every character |
| that's legal in such atoms, terminated by the valid atom terminator |
| TERM. */ |
| static void |
| put_implicit_length_all_chars (svn_stringbuf_t *str, char term) |
| { |
| int len; |
| char *name = gen_implicit_length_all_chars (&len); |
| |
| if (term != '\0' |
| && ! skel_is_space (term) |
| && ! skel_is_paren (term)) |
| abort (); |
| |
| svn_stringbuf_appendbytes (str, name, len); |
| if (term != '\0') |
| svn_stringbuf_appendbytes (str, &term, 1); |
| } |
| |
| |
| /* Return true iff SKEL is the parsed form of the atom produced by |
| calling put_implicit_length_all_chars. */ |
| static int |
| check_implicit_length_all_chars (skel_t *skel) |
| { |
| int len; |
| char *name = gen_implicit_length_all_chars (&len); |
| |
| return check_atom (skel, name, len); |
| } |
| |
| |
| |
| /* Test parsing of implicit-length atoms. */ |
| |
| static svn_error_t * |
| parse_implicit_length (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) |
| { |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *skel; |
| |
| *msg = "parse implicit-length atoms"; |
| |
| if (msg_only) |
| return SVN_NO_ERROR; |
| |
| /* Try all valid single-byte atoms. */ |
| { |
| const char *c; |
| int i; |
| |
| for (c = "\t\n\f\r ()[]"; *c; c++) |
| for (i = 0; i < 256; i++) |
| if (skel_is_name((apr_byte_t)i)) |
| { |
| svn_stringbuf_setempty (str); |
| put_implicit_length_byte (str, (apr_byte_t)i, *c); |
| skel = parse_str (str, pool); |
| if (! check_implicit_length_byte (skel, (apr_byte_t)i)) |
| return fail (pool, "single-byte implicit-length skel 0x%02x" |
| " with terminator 0x%02x", |
| i, c); |
| } |
| } |
| |
| /* Try an atom that contains every character that's legal in an |
| implicit-length atom. */ |
| svn_stringbuf_setempty (str); |
| put_implicit_length_all_chars (str, '\0'); |
| skel = parse_str (str, pool); |
| if (! check_implicit_length_all_chars (skel)) |
| return fail (pool, "implicit-length skel containing all legal chars"); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Functions that generate/check interesting explicit-length atoms. */ |
| |
| |
| /* Append to STR the representation of the atom containing the LEN |
| bytes at DATA, in explicit-length form, using SEP as the separator |
| between the length and the data. */ |
| static void |
| put_explicit_length (svn_stringbuf_t *str, const char *data, int len, char sep) |
| { |
| char *buf = malloc (len + 100); |
| int length_len; |
| |
| if (! skel_is_space (sep)) |
| abort (); |
| |
| /* Generate the length and separator character. */ |
| sprintf (buf, "%d%c", len, sep); |
| length_len = strlen(buf); |
| |
| /* Copy in the real data (which may contain nulls). */ |
| memcpy (buf + length_len, data, len); |
| |
| svn_stringbuf_appendbytes (str, buf, length_len + len); |
| free (buf); |
| } |
| |
| |
| /* Return true iff SKEL is the parsed form of an atom generated by |
| put_explicit_length. */ |
| static int |
| check_explicit_length (skel_t *skel, const char *data, int len) |
| { |
| return check_atom (skel, data, len); |
| } |
| |
| |
| /* Test parsing of explicit-length atoms. */ |
| |
| static svn_error_t * |
| try_explicit_length (const char *data, int len, int check_len, |
| apr_pool_t *pool) |
| { |
| int i; |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *skel; |
| |
| /* Try it with every possible separator character. */ |
| for (i = 0; i < 256; i++) |
| if (skel_is_space ( (apr_byte_t)i)) |
| { |
| svn_stringbuf_setempty (str); |
| put_explicit_length (str, data, len, (apr_byte_t)i); |
| skel = parse_str (str, pool); |
| if (! check_explicit_length (skel, data, check_len)) |
| return fail (pool, "failed to reparse explicit-length atom"); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| parse_explicit_length (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) |
| { |
| *msg = "parse explicit-length atoms"; |
| |
| if (msg_only) |
| return SVN_NO_ERROR; |
| |
| /* Try to parse the empty atom. */ |
| SVN_ERR (try_explicit_length ("", 0, 0, pool)); |
| |
| /* Try to parse every one-character atom. */ |
| { |
| int i; |
| |
| for (i = 0; i < 256; i++) |
| { |
| char buf[1]; |
| |
| buf[0] = i; |
| SVN_ERR (try_explicit_length (buf, 1, 1, pool)); |
| } |
| } |
| |
| /* Try to parse an atom containing every character. */ |
| { |
| int i; |
| char data[256]; |
| |
| for (i = 0; i < 256; i++) |
| data[i] = i; |
| |
| SVN_ERR (try_explicit_length (data, 256, 256, pool)); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* Test parsing of invalid atoms. */ |
| |
| static struct invalid_atoms |
| { |
| int type; |
| int len; |
| const char *data; |
| } invalid_atoms[] = { { 1, 1, "(" }, |
| { 1, 1, ")" }, |
| { 1, 1, "[" }, |
| { 1, 1, "]" }, |
| { 1, 1, " " }, |
| { 1, 13, "Hello, World!" }, |
| { 1, 8, "1mplicit" }, |
| |
| { 2, 2, "1" }, |
| { 2, 1, "12" }, |
| |
| { 7, 0, NULL } }; |
| |
| static svn_error_t * |
| parse_invalid_atoms (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) |
| { |
| struct invalid_atoms *ia = invalid_atoms; |
| |
| *msg = "parse invalid atoms"; |
| |
| if (msg_only) |
| return SVN_NO_ERROR; |
| |
| while (ia->type != 7) |
| { |
| if (ia->type == 1) |
| { |
| skel_t *skel = parse_cstr ((char *) ia->data, pool); |
| if (check_atom (skel, ia->data, ia->len)) |
| return fail (pool, |
| "failed to detect parsing error in `%s'", ia->data); |
| } |
| else |
| if (try_explicit_length (ia->data, ia->len, strlen (ia->data), pool) |
| == SVN_NO_ERROR) |
| fail (pool, "got wrong length in explicit-length atom"); |
| |
| ia++; |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* Functions that generate/check interesting lists. */ |
| |
| /* Append the start of a list to STR, using LEN bytes of the |
| whitespace character SPACE. */ |
| static void |
| put_list_start (svn_stringbuf_t *str, char space, int len) |
| { |
| int i; |
| |
| if (len > 0 && ! skel_is_space (space)) |
| abort (); |
| |
| svn_stringbuf_appendcstr (str, "("); |
| for (i = 0; i < len; i++) |
| svn_stringbuf_appendbytes (str, &space, 1); |
| } |
| |
| |
| /* Append the end of a list to STR, using LEN bytes of the |
| whitespace character SPACE. */ |
| static void |
| put_list_end (svn_stringbuf_t *str, char space, int len) |
| { |
| int i; |
| |
| if (len > 0 && ! skel_is_space (space)) |
| abort (); |
| |
| for (i = 0; i < len; i++) |
| svn_stringbuf_appendbytes (str, &space, 1); |
| svn_stringbuf_appendcstr (str, ")"); |
| } |
| |
| |
| /* Return true iff SKEL is a list of length DESIRED_LEN. */ |
| static int |
| check_list (skel_t *skel, int desired_len) |
| { |
| int len; |
| skel_t *child; |
| |
| if (! (skel |
| && ! skel->is_atom)) |
| return 0; |
| |
| len = 0; |
| for (child = skel->children; child; child = child->next) |
| len++; |
| |
| return len == desired_len; |
| } |
| |
| |
| |
| /* Parse lists. */ |
| |
| static svn_error_t * |
| parse_list (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) |
| { |
| *msg = "parse lists"; |
| |
| if (msg_only) |
| return SVN_NO_ERROR; |
| |
| { |
| /* Try lists of varying length. */ |
| int list_len; |
| |
| for (list_len = 0; |
| list_len < 30; |
| list_len < 4 ? list_len++ : (list_len *= 3)) |
| { |
| /* Try lists with different separators. */ |
| int sep; |
| |
| for (sep = 0; sep < 256; sep++) |
| if (skel_is_space ( (apr_byte_t)sep)) |
| { |
| /* Try lists with different numbers of separator |
| characters between the elements. */ |
| int sep_count; |
| |
| for (sep_count = 0; |
| sep_count < 30; |
| sep_count < 4 ? sep_count++ : (sep_count *= 3)) |
| { |
| /* Try various single-byte implicit-length atoms |
| for elements. */ |
| int atom_byte; |
| |
| for (atom_byte = 0; atom_byte < 256; atom_byte++) |
| if (skel_is_name ( (apr_byte_t)atom_byte)) |
| { |
| int i; |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *skel; |
| skel_t *child; |
| |
| put_list_start (str, (apr_byte_t)sep, sep_count); |
| for (i = 0; i < list_len; i++) |
| put_implicit_length_byte (str, (apr_byte_t)atom_byte, (apr_byte_t)sep); |
| put_list_end (str, (apr_byte_t)sep, sep_count); |
| |
| skel = parse_str (str, pool); |
| if (! check_list (skel, list_len)) |
| return fail (pool, "couldn't parse list"); |
| for (child = skel->children; |
| child; |
| child = child->next) |
| if (! check_implicit_length_byte (child, (apr_byte_t)atom_byte)) |
| return fail (pool, "list was reparsed incorrectly"); |
| } |
| |
| /* Try the atom containing every character that's |
| legal in an implicit-length atom as the element. */ |
| { |
| int i; |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *skel; |
| skel_t *child; |
| |
| put_list_start (str, (apr_byte_t)sep, sep_count); |
| for (i = 0; i < list_len; i++) |
| put_implicit_length_all_chars (str, (apr_byte_t)sep); |
| put_list_end (str, (apr_byte_t)sep, sep_count); |
| |
| skel = parse_str (str, pool); |
| if (! check_list (skel, list_len)) |
| return fail (pool, "couldn't parse list"); |
| for (child = skel->children; |
| child; |
| child = child->next) |
| if (! check_implicit_length_all_chars (child)) |
| return fail (pool, "couldn't parse list"); |
| } |
| |
| /* Try using every one-byte explicit-length atom as |
| an element. */ |
| for (atom_byte = 0; atom_byte < 256; atom_byte++) |
| { |
| int i; |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *skel; |
| skel_t *child; |
| char buf[1]; |
| |
| buf[0] = atom_byte; |
| |
| put_list_start (str, (apr_byte_t)sep, sep_count); |
| for (i = 0; i < list_len; i++) |
| put_explicit_length (str, buf, 1, (apr_byte_t)sep); |
| put_list_end (str, (apr_byte_t)sep, sep_count); |
| |
| skel = parse_str (str, pool); |
| if (! check_list (skel, list_len)) |
| return fail (pool, "couldn't parse list"); |
| for (child = skel->children; |
| child; |
| child = child->next) |
| if (! check_explicit_length (child, buf, 1)) |
| return fail (pool, "list was reparsed incorrectly"); |
| } |
| |
| /* Try using an atom containing every character as |
| an element. */ |
| { |
| int i; |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *skel; |
| skel_t *child; |
| char data[256]; |
| |
| for (i = 0; i < 256; i++) |
| data[i] = i; |
| |
| put_list_start (str, (apr_byte_t)sep, sep_count); |
| for (i = 0; i < list_len; i++) |
| put_explicit_length (str, data, 256, (apr_byte_t)sep); |
| put_list_end (str, (apr_byte_t)sep, sep_count); |
| |
| skel = parse_str (str, pool); |
| if (! check_list (skel, list_len)) |
| return fail (pool, "couldn't parse list"); |
| for (child = skel->children; |
| child; |
| child = child->next) |
| if (! check_explicit_length (child, data, 256)) |
| return fail (pool, "list was re-parsed incorrectly"); |
| } |
| } |
| } |
| } |
| } |
| |
| /* Try to parse some invalid lists. */ |
| { |
| int sep; |
| |
| /* Try different separators. */ |
| for (sep = 0; sep < 256; sep++) |
| if (skel_is_space ( (apr_byte_t)sep)) |
| { |
| /* Try lists with different numbers of separator |
| characters between the elements. */ |
| int sep_count; |
| |
| for (sep_count = 0; |
| sep_count < 100; |
| sep_count < 10 ? sep_count++ : (sep_count *= 3)) |
| { |
| svn_stringbuf_t *str; |
| |
| /* A list with only a separator. */ |
| str = get_empty_string (pool); |
| put_list_start (str, (apr_byte_t)sep, sep_count); |
| if (parse_str (str, pool)) |
| return fail (pool, "failed to detect syntax error"); |
| |
| /* A list with only a terminator. */ |
| str = get_empty_string (pool); |
| put_list_end (str, (apr_byte_t)sep, sep_count); |
| if (parse_str (str, pool)) |
| return fail (pool, "failed to detect syntax error"); |
| |
| /* A list containing an invalid element. */ |
| str = get_empty_string (pool); |
| put_list_start (str, (apr_byte_t)sep, sep_count); |
| svn_stringbuf_appendcstr (str, "100 "); |
| put_list_end (str, (apr_byte_t)sep, sep_count); |
| if (parse_str (str, pool)) |
| return fail (pool, "failed to detect invalid element"); |
| } |
| } |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* Building interesting skels. */ |
| |
| /* Build an atom skel containing the LEN bytes at DATA. */ |
| static skel_t * |
| build_atom (apr_size_t len, char *data, apr_pool_t *pool) |
| { |
| char *copy = apr_palloc (pool, len); |
| skel_t *skel = apr_palloc (pool, sizeof (*skel)); |
| |
| memcpy (copy, data, len); |
| skel->is_atom = 1; |
| skel->len = len; |
| skel->data = copy; |
| |
| return skel; |
| } |
| |
| /* Build an empty list skel. */ |
| static skel_t * |
| empty (apr_pool_t *pool) |
| { |
| skel_t *skel = apr_palloc (pool, sizeof (*skel)); |
| |
| skel->is_atom = 0; |
| skel->children = 0; |
| |
| return skel; |
| } |
| |
| /* Stick ELEMENT at the beginning of the list skeleton LIST. */ |
| static void |
| add (skel_t *element, skel_t *list) |
| { |
| element->next = list->children; |
| list->children = element; |
| } |
| |
| |
| /* Return true if the contents of skel A are identical to those of |
| skel B. */ |
| static int |
| skel_equal (skel_t *a, skel_t *b) |
| { |
| if (a->is_atom != b->is_atom) |
| return 0; |
| |
| if (a->is_atom) |
| return (a->len == b->len |
| && ! memcmp (a->data, b->data, a->len)); |
| else |
| { |
| skel_t *a_child, *b_child; |
| |
| for (a_child = a->children, b_child = b->children; |
| a_child && b_child; |
| a_child = a_child->next, b_child = b_child->next) |
| if (! skel_equal (a_child, b_child)) |
| return 0; |
| |
| if (a_child || b_child) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| |
| /* Unparsing implicit-length atoms. */ |
| |
| static svn_error_t * |
| unparse_implicit_length (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) |
| { |
| *msg = "unparse implicit-length atoms"; |
| |
| if (msg_only) |
| return SVN_NO_ERROR; |
| |
| /* Unparse and check every single-byte implicit-length atom. */ |
| { |
| int byte; |
| |
| for (byte = 0; byte < 256; byte++) |
| if (skel_is_name ( (apr_byte_t)byte)) |
| { |
| svn_stringbuf_t *str = get_empty_string (pool); |
| char buf = (char)byte; |
| skel_t *skel = build_atom (1, &buf, pool); |
| |
| str = svn_fs__unparse_skel (skel, pool); |
| |
| if (! (str |
| && str->len == 1 |
| && str->data[0] == (char)byte)) |
| return fail (pool, "incorrectly unparsed single-byte " |
| "implicit-length atom"); |
| } |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| /* Unparse some lists. */ |
| |
| static svn_error_t * |
| unparse_list (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) |
| { |
| *msg = "unparse lists"; |
| |
| if (msg_only) |
| return SVN_NO_ERROR; |
| |
| /* Make a list of all the single-byte implicit-length atoms. */ |
| { |
| svn_stringbuf_t *str = get_empty_string (pool); |
| int byte; |
| skel_t *list = empty (pool); |
| skel_t *reparsed, *elt; |
| |
| for (byte = 0; byte < 256; byte++) |
| if (skel_is_name ( (apr_byte_t)byte)) |
| { |
| char buf = byte; |
| add (build_atom (1, &buf, pool), list); |
| } |
| |
| /* Unparse that, parse it again, and see if we got the same thing |
| back. */ |
| str = svn_fs__unparse_skel (list, pool); |
| reparsed = svn_fs__parse_skel (str->data, str->len, pool); |
| |
| if (! reparsed || reparsed->is_atom) |
| return fail (pool, "result is syntactically misformed, or not a list"); |
| |
| if (! skel_equal (list, reparsed)) |
| return fail (pool, "unparsing and parsing didn't preserve contents"); |
| |
| elt = reparsed->children; |
| for (byte = 255; byte >= 0; byte--) |
| if (skel_is_name ( (apr_byte_t)byte)) |
| { |
| if (! (elt |
| && elt->is_atom |
| && elt->len == 1 |
| && elt->data[0] == byte)) |
| return fail (pool, "bad element"); |
| |
| /* Verify that each element's data falls within the string. */ |
| if (elt->data < str->data |
| || elt->data + elt->len > str->data + str->len) |
| return fail (pool, "bad element"); |
| |
| elt = elt->next; |
| } |
| |
| /* We should have reached the end of the list at this point. */ |
| if (elt) |
| return fail (pool, "list too long"); |
| } |
| |
| /* Make a list of lists. */ |
| { |
| svn_stringbuf_t *str = get_empty_string (pool); |
| skel_t *top = empty (pool); |
| skel_t *reparsed; |
| int i; |
| |
| for (i = 0; i < 10; i++) |
| { |
| skel_t *middle = empty (pool); |
| int j; |
| |
| for (j = 0; j < 10; j++) |
| { |
| char buf[10]; |
| int k, val; |
| |
| /* Make some interesting atom, containing lots of binary |
| characters. */ |
| val = i * 10 + j; |
| for (k = 0; k < sizeof (buf); k++) |
| { |
| buf[k] = val; |
| val += j; |
| } |
| |
| add (build_atom (sizeof (buf), buf, pool), middle); |
| } |
| |
| add (middle, top); |
| } |
| |
| str = svn_fs__unparse_skel (top, pool); |
| reparsed = svn_fs__parse_skel (str->data, str->len, pool); |
| |
| if (! skel_equal (top, reparsed)) |
| return fail (pool, "failed to reparse list of lists"); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* The test table. */ |
| |
| svn_error_t *(*test_funcs[]) (const char **msg, |
| svn_boolean_t msg_only, |
| apr_pool_t *pool) = |
| { |
| 0, |
| parse_implicit_length, |
| parse_explicit_length, |
| parse_invalid_atoms, |
| parse_list, |
| unparse_implicit_length, |
| unparse_list, |
| 0 |
| }; |