MAX_MCS_INDEX is a valid array index.
[metze/wireshark/wip.git] / epan / tvbuff.c
index 28e22895c1b6dd459652dee6f9754fae3cb7a833..12378e644139135f1353a2fed555990465614c2d 100644 (file)
@@ -9,8 +9,6 @@
  *             the data of a backing tvbuff, or can be a composite of
  *             other tvbuffs.
  *
- * $Id$
- *
  * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
  *
  * Code to convert IEEE floating point formats to native floating point
 #include "config.h"
 
 #include <string.h>
+#include <stdio.h>
+#include <errno.h>
 
 #include "wsutil/pint.h"
 #include "wsutil/sign_ext.h"
+#include "wsutil/unicode-utils.h"
+#include "wsutil/nstime.h"
+#include "wsutil/time_util.h"
 #include "tvbuff.h"
 #include "tvbuff-int.h"
 #include "strutil.h"
 #include "proto.h"     /* XXX - only used for DISSECTOR_ASSERT, probably a new header file? */
 #include "exceptions.h"
 
+/*
+ * Just make sure we include the prototype for strptime as well
+ * (needed for glibc 2.2) but make sure we do this only if not
+ * yet defined.
+ */
+#include <time.h>
+/*#ifndef HAVE_STRPTIME*/
+#ifndef strptime
+#include "wsutil/strptime.h"
+#endif
+ /*#endif*/
+
 static guint64
 _tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits);
 
+static inline gint
+_tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset);
+
+static inline const guint8*
+ensure_contiguous(tvbuff_t *tvb, const gint offset, const gint length);
+
+static inline guint8 *
+tvb_get_raw_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length);
+
 tvbuff_t *
 tvb_new(const struct tvb_ops *ops)
 {
@@ -158,7 +182,7 @@ validate_offset(const tvbuff_t *tvb, const guint abs_offset)
                return ReportedBoundsError;
 }
 
-static int
+static inline int
 compute_offset(const tvbuff_t *tvb, const gint offset, guint *offset_ptr)
 {
        if (offset >= 0) {
@@ -189,7 +213,7 @@ compute_offset(const tvbuff_t *tvb, const gint offset, guint *offset_ptr)
        return 0;
 }
 
-static int
+static inline int
 compute_offset_and_remaining(const tvbuff_t *tvb, const gint offset, guint *offset_ptr, guint *rem_len)
 {
        int exception;
@@ -214,13 +238,13 @@ compute_offset_and_remaining(const tvbuff_t *tvb, const gint offset, guint *offs
  * left for the next protocol - we want the next protocol to be the one
  * that gets an exception, so the error is reported as an error in that
  * protocol rather than the containing protocol.  */
-static int
+static inline int
 check_offset_length_no_exception(const tvbuff_t *tvb,
                                 const gint offset, gint const length_val,
                                 guint *offset_ptr, guint *length_ptr)
 {
        guint end_offset;
-       int exception;
+       int   exception;
 
        DISSECTOR_ASSERT(offset_ptr);
        DISSECTOR_ASSERT(length_ptr);
@@ -258,7 +282,7 @@ check_offset_length_no_exception(const tvbuff_t *tvb,
 /* Checks (+/-) offset and length and throws an exception if
  * either is out of bounds. Sets integer ptrs to the new offset
  * and length. */
-static void
+static inline void
 check_offset_length(const tvbuff_t *tvb,
                    const gint offset, gint const length_val,
                    guint *offset_ptr, guint *length_ptr)
@@ -298,12 +322,14 @@ tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
        guint8        left, right, remaining_bits, *buf;
        const guint8 *data;
 
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
+
        byte_offset = bit_offset >> 3;
        left = bit_offset % 8; /* for left-shifting */
        right = 8 - left; /* for right-shifting */
 
        if (no_of_bits == -1) {
-               datalen = tvb_length_remaining(tvb, byte_offset);
+               datalen = _tvb_captured_length_remaining(tvb, byte_offset);
                remaining_bits = 0;
        } else {
                datalen = no_of_bits >> 3;
@@ -315,18 +341,18 @@ tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
 
        /* already aligned -> shortcut */
        if ((left == 0) && (remaining_bits == 0)) {
-               return tvb_new_subset(tvb, byte_offset, datalen, -1);
+               return tvb_new_subset(tvb, byte_offset, datalen, datalen);
        }
 
        DISSECTOR_ASSERT(datalen>0);
 
        /* if at least one trailing byte is available, we must use the content
-       * of that byte for the last shift (i.e. tvb_get_ptr() must use datalen + 1
-       * if non extra byte is available, the last shifted byte requires
-       * special treatment
-       */
-       if (tvb_length_remaining(tvb, byte_offset) > datalen) {
-               data = tvb_get_ptr(tvb, byte_offset, datalen + 1);
+       * of that byte for the last shift (i.e. tvb_get_ptr() must use datalen + 1
+       * if non extra byte is available, the last shifted byte requires
+       * special treatment
+       */
+       if (_tvb_captured_length_remaining(tvb, byte_offset) > datalen) {
+               data = ensure_contiguous(tvb, byte_offset, datalen + 1); /* tvb_get_ptr */
 
                /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
                buf = (guint8 *)g_malloc(datalen);
@@ -335,7 +361,7 @@ tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
                for (i = 0; i < datalen; i++)
                        buf[i] = (data[i] << left) | (data[i+1] >> right);
        } else {
-               data = tvb_get_ptr(tvb, byte_offset, datalen);
+               data = ensure_contiguous(tvb, byte_offset, datalen); /* tvb_get_ptr() */
 
                /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
                buf = (guint8 *)g_malloc(datalen);
@@ -357,8 +383,11 @@ static tvbuff_t *
 tvb_generic_clone_offset_len(tvbuff_t *tvb, guint offset, guint len)
 {
        tvbuff_t *cloned_tvb;
+       guint8 *data;
 
-       guint8 *data = (guint8 *) g_malloc(len);
+       DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));
+
+       data = (guint8 *) g_malloc(len);
 
        tvb_memcpy(tvb, data, offset, len);
 
@@ -389,18 +418,32 @@ tvb_clone(tvbuff_t *tvb)
 }
 
 guint
-tvb_length(const tvbuff_t *tvb)
+tvb_captured_length(const tvbuff_t *tvb)
 {
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
        return tvb->length;
 }
 
+/* For tvbuff internal use */
+static inline gint
+_tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset)
+{
+       guint abs_offset, rem_length;
+       int   exception;
+
+       exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
+       if (exception)
+               return 0;
+
+       return rem_length;
+}
+
 gint
-tvb_length_remaining(const tvbuff_t *tvb, const gint offset)
+tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset)
 {
        guint abs_offset, rem_length;
-       int exception;
+       int   exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -412,9 +455,9 @@ tvb_length_remaining(const tvbuff_t *tvb, const gint offset)
 }
 
 guint
-tvb_ensure_length_remaining(const tvbuff_t *tvb, const gint offset)
+tvb_ensure_captured_length_remaining(const tvbuff_t *tvb, const gint offset)
 {
-       guint abs_offset, rem_length;
+       guint abs_offset = 0, rem_length = 0;
        int   exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
@@ -450,7 +493,7 @@ gboolean
 tvb_bytes_exist(const tvbuff_t *tvb, const gint offset, const gint length)
 {
        guint abs_offset, abs_length;
-       int exception;
+       int   exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -461,6 +504,24 @@ tvb_bytes_exist(const tvbuff_t *tvb, const gint offset, const gint length)
        return TRUE;
 }
 
+/* Validates that 'length' bytes, where 'length' is a 64-bit unsigned
+ * integer, are available starting from offset (pos/neg). Throws an
+ * exception if they aren't. */
+void
+tvb_ensure_bytes_exist64(const tvbuff_t *tvb, const gint offset, const guint64 length)
+{
+       /*
+        * Make sure the value fits in a signed integer; if not, assume
+        * that means that it's too big.
+        */
+       if (length > G_MAXINT) {
+               THROW(ReportedBoundsError);
+       }
+
+       /* OK, now cast it and try it with tvb_ensure_bytes_exist(). */
+       tvb_ensure_bytes_exist(tvb, offset, (gint)length);
+}
+
 /* Validates that 'length' bytes are available starting from
  * offset (pos/neg). Throws an exception if they aren't. */
 void
@@ -539,7 +600,7 @@ gboolean
 tvb_offset_exists(const tvbuff_t *tvb, const gint offset)
 {
        guint abs_offset;
-       int exception;
+       int   exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -570,7 +631,7 @@ gint
 tvb_reported_length_remaining(const tvbuff_t *tvb, const gint offset)
 {
        guint abs_offset;
-       int exception;
+       int   exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -618,11 +679,11 @@ tvb_offset_from_real_beginning(const tvbuff_t *tvb)
        return tvb_offset_from_real_beginning_counter(tvb, 0);
 }
 
-static const guint8*
+static inline const guint8*
 ensure_contiguous_no_exception(tvbuff_t *tvb, const gint offset, const gint length, int *pexception)
 {
-       guint abs_offset, abs_length;
-       int exception;
+       guint abs_offset = 0, abs_length = 0;
+       int   exception;
 
        exception = check_offset_length_no_exception(tvb, offset, length, &abs_offset, &abs_length);
        if (exception) {
@@ -645,7 +706,7 @@ ensure_contiguous_no_exception(tvbuff_t *tvb, const gint offset, const gint leng
        return NULL;
 }
 
-static const guint8*
+static inline const guint8*
 ensure_contiguous(tvbuff_t *tvb, const gint offset, const gint length)
 {
        int           exception = 0;
@@ -659,7 +720,7 @@ ensure_contiguous(tvbuff_t *tvb, const gint offset, const gint length)
        return p;
 }
 
-static const guint8*
+static inline const guint8*
 fast_ensure_contiguous(tvbuff_t *tvb, const gint offset, const guint length)
 {
        guint end_offset;
@@ -693,28 +754,6 @@ fast_ensure_contiguous(tvbuff_t *tvb, const gint offset, const guint length)
        return NULL;
 }
 
-static const guint8*
-guint8_pbrk(const guint8* haystack, size_t haystacklen, const guint8 *needles, guchar *found_needle)
-{
-       gchar         tmp[256] = { 0 };
-       const guint8 *haystack_end;
-
-       while (*needles)
-               tmp[*needles++] = 1;
-
-       haystack_end = haystack + haystacklen;
-       while (haystack < haystack_end) {
-               if (tmp[*haystack]) {
-                       if (found_needle)
-                               *found_needle = *haystack;
-                       return haystack;
-               }
-               haystack++;
-       }
-
-       return NULL;
-}
-
 
 
 /************** ACCESSORS **************/
@@ -722,7 +761,7 @@ guint8_pbrk(const guint8* haystack, size_t haystacklen, const guint8 *needles, g
 void *
 tvb_memcpy(tvbuff_t *tvb, void *target, const gint offset, size_t length)
 {
-       guint   abs_offset, abs_length;
+       guint   abs_offset = 0, abs_length = 0;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -747,9 +786,17 @@ tvb_memcpy(tvbuff_t *tvb, void *target, const gint offset, size_t length)
        if (tvb->ops->tvb_memcpy)
                return tvb->ops->tvb_memcpy(tvb, target, abs_offset, abs_length);
 
-       /* XXX, fallback to slower method */
-
-       DISSECTOR_ASSERT_NOT_REACHED();
+       /*
+        * If the length is 0, there's nothing to do.
+        * (tvb->real_data could be null if it's allocated with
+        * a size of length.)
+        */
+       if (length != 0) {
+               /*
+                * XXX, fallback to slower method
+                */
+               DISSECTOR_ASSERT_NOT_REACHED();
+       }
        return NULL;
 }
 
@@ -765,15 +812,15 @@ tvb_memcpy(tvbuff_t *tvb, void *target, const gint offset, size_t length)
  * meaning "to the end of the buffer"?
  *
  * If scope is NULL, memory is allocated with g_malloc() and user must
- * explicitely free it with g_free().
+ * explicitly free it with g_free().
  * If scope is not NULL, memory is allocated with the corresponding pool
  * lifetime.
  */
 void *
 tvb_memdup(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, size_t length)
 {
-       guint   abs_offset, abs_length;
-       void    *duped;
+       guint  abs_offset = 0, abs_length = 0;
+       void  *duped;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -894,6 +941,114 @@ tvb_get_ntoh64(tvbuff_t *tvb, const gint offset)
        return pntoh64(ptr);
 }
 
+guint16
+tvb_get_guint16(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohs(tvb, offset);
+       } else {
+               return tvb_get_ntohs(tvb, offset);
+       }
+}
+
+guint32
+tvb_get_guint24(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letoh24(tvb, offset);
+       } else {
+               return tvb_get_ntoh24(tvb, offset);
+       }
+}
+
+guint32
+tvb_get_guint32(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohl(tvb, offset);
+       } else {
+               return tvb_get_ntohl(tvb, offset);
+       }
+}
+
+guint64
+tvb_get_guint40(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letoh40(tvb, offset);
+       } else {
+               return tvb_get_ntoh40(tvb, offset);
+       }
+}
+
+gint64
+tvb_get_gint40(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohi40(tvb, offset);
+       } else {
+               return tvb_get_ntohi40(tvb, offset);
+       }
+}
+
+guint64
+tvb_get_guint48(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letoh48(tvb, offset);
+       } else {
+               return tvb_get_ntoh48(tvb, offset);
+       }
+}
+
+gint64
+tvb_get_gint48(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohi48(tvb, offset);
+       } else {
+               return tvb_get_ntohi48(tvb, offset);
+       }
+}
+
+guint64
+tvb_get_guint56(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letoh56(tvb, offset);
+       } else {
+               return tvb_get_ntoh56(tvb, offset);
+       }
+}
+
+gint64
+tvb_get_gint56(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohi56(tvb, offset);
+       } else {
+               return tvb_get_ntohi56(tvb, offset);
+       }
+}
+
+guint64
+tvb_get_guint64(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letoh64(tvb, offset);
+       } else {
+               return tvb_get_ntoh64(tvb, offset);
+       }
+}
+
+gfloat
+tvb_get_ieee_float(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohieee_float(tvb, offset);
+       } else {
+               return tvb_get_ntohieee_float(tvb, offset);
+       }
+}
+
+gdouble
+tvb_get_ieee_double(tvbuff_t *tvb, const gint offset, const guint encoding) {
+       if (encoding & ENC_LITTLE_ENDIAN) {
+               return tvb_get_letohieee_double(tvb, offset);
+       } else {
+               return tvb_get_ntohieee_double(tvb, offset);
+       }
+}
+
 /*
  * Stuff for IEEE float handling on platforms that don't have IEEE
  * format as the native floating-point format.
@@ -1055,7 +1210,7 @@ tvb_get_ntohieee_float(tvbuff_t *tvb, const int offset)
        return get_ieee_float(tvb_get_ntohl(tvb, offset));
 #else
        union {
-               gfloat f;
+               gfloat  f;
                guint32 w;
        } ieee_fp_union;
 
@@ -1247,6 +1402,245 @@ tvb_get_letohieee_double(tvbuff_t *tvb, const int offset)
 #endif
 }
 
+static inline void
+validate_single_byte_ascii_encoding(const guint encoding)
+{
+       const guint enc = encoding & ~ENC_STR_MASK;
+
+       switch (enc) {
+           case ENC_UTF_16:
+           case ENC_UCS_2:
+           case ENC_UCS_4:
+           case ENC_3GPP_TS_23_038_7BITS:
+           case ENC_EBCDIC:
+               REPORT_DISSECTOR_BUG("Invalid string encoding type passed to tvb_get_string_XXX");
+               break;
+           default:
+               break;
+       }
+       /* make sure something valid was set */
+       if (enc == 0)
+           REPORT_DISSECTOR_BUG("No string encoding type passed to tvb_get_string_XXX");
+}
+
+GByteArray*
+tvb_get_string_bytes(tvbuff_t *tvb, const gint offset, const gint length,
+                    const guint encoding, GByteArray *bytes, gint *endoff)
+{
+       const gchar *ptr    = (gchar*) tvb_get_raw_string(wmem_packet_scope(), tvb, offset, length);
+       const gchar *begin  = ptr;
+       const gchar *end    = NULL;
+       GByteArray  *retval = NULL;
+
+       errno = EDOM;
+
+       validate_single_byte_ascii_encoding(encoding);
+
+       if (endoff) *endoff = 0;
+
+       while (*begin == ' ') begin++;
+
+       if (*begin && bytes) {
+               if (hex_str_to_bytes_encoding(begin, bytes, &end, encoding, FALSE)) {
+                       if (bytes->len > 0) {
+                               if (endoff) *endoff = offset + (gint)(end - ptr);
+                               errno = 0;
+                               retval = bytes;
+                       }
+               }
+       }
+
+       return retval;
+}
+
+/* support hex-encoded time values? */
+nstime_t*
+tvb_get_string_time(tvbuff_t *tvb, const gint offset, const gint length,
+                   const guint encoding, nstime_t *ns, gint *endoff)
+{
+       const gchar *begin     = (gchar*) tvb_get_raw_string(wmem_packet_scope(), tvb, offset, length);
+       const gchar *ptr       = begin;
+       const gchar *end       = NULL;
+       struct tm    tm;
+       nstime_t*    retval    = NULL;
+       char         sign      = '+';
+       int          off_hr    = 0;
+       int          off_min   = 0;
+       int          num_chars = 0;
+       gboolean     matched   = FALSE;
+
+       errno = EDOM;
+
+       validate_single_byte_ascii_encoding(encoding);
+
+       DISSECTOR_ASSERT(ns);
+
+       memset(&tm, 0, sizeof(tm));
+       tm.tm_isdst = -1;
+       ns->secs    = 0;
+       ns->nsecs   = 0;
+
+       while (*ptr == ' ') ptr++;
+
+       if (*ptr) {
+               /* note: sscanf is known to be inconsistent across platforms with respect
+                  to whether a %n is counted as a return value or not, so we have to use
+                  '>=' a lot */
+               if ((encoding & ENC_ISO_8601_DATE_TIME) == ENC_ISO_8601_DATE_TIME) {
+                       /* TODO: using sscanf this many times is probably slow; might want
+                          to parse it by hand in the future */
+                       /* 2014-04-07T05:41:56+00:00 */
+                       if (sscanf(ptr, "%d-%d-%d%*c%d:%d:%d%c%d:%d%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &tm.tm_sec,
+                           &sign,
+                           &off_hr,
+                           &off_min,
+                           &num_chars) >= 9)
+                       {
+                               matched = TRUE;
+                       }
+                       /* no seconds is ok */
+                       else if (sscanf(ptr, "%d-%d-%d%*c%d:%d%c%d:%d%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &sign,
+                           &off_hr,
+                           &off_min,
+                           &num_chars) >= 8)
+                       {
+                               matched = TRUE;
+                       }
+                       /* 2007-04-05T14:30:56Z */
+                       else if (sscanf(ptr, "%d-%d-%d%*c%d:%d:%dZ%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &tm.tm_sec,
+                           &num_chars) >= 6)
+                       {
+                               matched = TRUE;
+                               off_hr = 0;
+                               off_min = 0;
+                       }
+                       /* 2007-04-05T14:30Z no seconds is ok */
+                       else if (sscanf(ptr, "%d-%d-%d%*c%d:%dZ%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &num_chars) >= 5)
+                       {
+                               matched = TRUE;
+                               off_hr = 0;
+                               off_min = 0;
+                       }
+
+                       if (matched) {
+                               errno = 0;
+                               end = ptr + num_chars;
+                               tm.tm_mon--;
+                               if (tm.tm_year > 1900) tm.tm_year -= 1900;
+                               if (sign == '-') off_hr = -off_hr;
+                       }
+               }
+               else if (encoding & ENC_ISO_8601_DATE) {
+                       /* 2014-04-07 */
+                       if (sscanf(ptr, "%d-%d-%d%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &num_chars) >= 3)
+                       {
+                               errno = 0;
+                               end = ptr + num_chars;
+                               tm.tm_mon--;
+                               if (tm.tm_year > 1900) tm.tm_year -= 1900;
+                       }
+               }
+               else if (encoding & ENC_ISO_8601_TIME) {
+                       /* 2014-04-07 */
+                       if (sscanf(ptr, "%d:%d:%d%n",
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &tm.tm_sec,
+                           &num_chars) >= 2)
+                       {
+                               /* what should we do about day/month/year? */
+                               /* setting it to "now" for now */
+                               time_t time_now = time(NULL);
+                               struct tm *tm_now = gmtime(&time_now);
+                               tm.tm_year = tm_now->tm_year;
+                               tm.tm_mon  = tm_now->tm_mon;
+                               tm.tm_mday = tm_now->tm_mday;
+                               end = ptr + num_chars;
+                               errno = 0;
+
+                       }
+               }
+               else if (encoding & ENC_RFC_822 || encoding & ENC_RFC_1123) {
+                       if (encoding & ENC_RFC_822) {
+                               /* this will unfortunately match ENC_RFC_1123 style
+                                  strings too, partially - probably need to do this the long way */
+                               end = strptime(ptr, "%a, %d %b %y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%a, %d %b %y %H:%M", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %y %H:%M", &tm);
+                       }
+                       else if (encoding & ENC_RFC_1123) {
+                               end = strptime(ptr, "%a, %d %b %Y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%a, %d %b %Y %H:%M", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %Y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %Y %H:%M", &tm);
+                       }
+                       if (end) {
+                               errno = 0;
+                               if (*end == ' ') end++;
+                               if (g_ascii_strncasecmp(end, "UT", 2) == 0)
+                               {
+                                       end += 2;
+                               }
+                               else if (g_ascii_strncasecmp(end, "GMT", 3) == 0)
+                               {
+                                       end += 3;
+                               }
+                               else if (sscanf(end, "%c%2d%2d%n",
+                                   &sign,
+                                   &off_hr,
+                                   &off_min,
+                                   &num_chars) < 3)
+                               {
+                                       errno = ERANGE;
+                               }
+                               if (sign == '-') off_hr = -off_hr;
+                       }
+               }
+       }
+
+       if (errno == 0) {
+               ns->secs = mktime_utc (&tm);
+               if (off_hr > 0)
+                       ns->secs += (off_hr * 3600) + (off_min * 60);
+               else if (off_hr < 0)
+                       ns->secs -= ((-off_hr) * 3600) + (off_min * 60);
+               retval = ns;
+               if (endoff)
+                   *endoff = (gint)(offset + (end - begin));
+       }
+
+       return retval;
+}
+
 /* Fetch an IPv4 address, in network byte order.
  * We do *not* convert them to host byte order; we leave them in
  * network byte order. */
@@ -1298,12 +1692,12 @@ tvb_get_letohguid(tvbuff_t *tvb, const gint offset, e_guid_t *guid)
  * NOTE: to support code written when proto_tree_add_item() took a
  * gboolean as its last argument, with FALSE meaning "big-endian"
  * and TRUE meaning "little-endian", we treat any non-zero value of
- * "representation" as meaning "little-endian".
+ * "encoding" as meaning "little-endian".
  */
 void
-tvb_get_guid(tvbuff_t *tvb, const gint offset, e_guid_t *guid, const guint representation)
+tvb_get_guid(tvbuff_t *tvb, const gint offset, e_guid_t *guid, const guint encoding)
 {
-       if (representation) {
+       if (encoding) {
                tvb_get_letohguid(tvb, offset, guid);
        } else {
                tvb_get_ntohguid(tvb, offset, guid);
@@ -1362,8 +1756,8 @@ static guint64
 _tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits)
 {
        guint64 value;
-       guint octet_offset = bit_offset >> 3;
-       guint8 required_bits_in_first_octet = 8 - (bit_offset % 8);
+       guint   octet_offset = bit_offset >> 3;
+       guint8  required_bits_in_first_octet = 8 - (bit_offset % 8);
 
        if(required_bits_in_first_octet > total_no_of_bits)
        {
@@ -1445,7 +1839,7 @@ tvb_find_guint8_generic(tvbuff_t *tvb, guint abs_offset, guint limit, guint8 nee
        const guint8 *ptr;
        const guint8 *result;
 
-       ptr = tvb_get_ptr(tvb, abs_offset, limit);
+       ptr = ensure_contiguous(tvb, abs_offset, limit); /* tvb_get_ptr() */
 
        result = (const guint8 *) memchr(ptr, needle, limit);
        if (!result)
@@ -1465,25 +1859,18 @@ gint
 tvb_find_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const guint8 needle)
 {
        const guint8 *result;
-       guint         abs_offset;
-       guint         tvbufflen;
-       guint         limit;
+       guint         abs_offset = 0;
+       guint         limit = 0;
+       int           exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
-       check_offset_length(tvb, offset, -1, &abs_offset, &tvbufflen);
+       exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
+       if (exception)
+               THROW(exception);
 
        /* Only search to end of tvbuff, w/o throwing exception. */
-       if (maxlength == -1) {
-               /* No maximum length specified; search to end of tvbuff. */
-               limit = tvbufflen;
-       }
-       else if (tvbufflen < (guint) maxlength) {
-               /* Maximum length goes past end of tvbuff; search to end
-                  of tvbuff. */
-               limit = tvbufflen;
-       }
-       else {
+       if (limit > (guint) maxlength) {
                /* Maximum length doesn't go past end of tvbuff; search
                   to that value. */
                limit = maxlength;
@@ -1506,22 +1893,23 @@ tvb_find_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const gu
        return tvb_find_guint8_generic(tvb, offset, limit, needle);
 }
 
-static gint
-tvb_pbrk_guint8_generic(tvbuff_t *tvb, guint abs_offset, guint limit, const guint8 *needles, guchar *found_needle)
+static inline gint
+tvb_ws_mempbrk_guint8_generic(tvbuff_t *tvb, guint abs_offset, guint limit, const ws_mempbrk_pattern* pattern, guchar *found_needle)
 {
        const guint8 *ptr;
        const guint8 *result;
 
-       ptr = tvb_get_ptr(tvb, abs_offset, limit);
+       ptr = ensure_contiguous(tvb, abs_offset, limit); /* tvb_get_ptr */
 
-       result = guint8_pbrk(ptr, limit, needles, found_needle);
+       result = ws_mempbrk_exec(ptr, limit, pattern, found_needle);
        if (!result)
                return -1;
 
        return (gint) ((result - ptr) + abs_offset);
 }
 
-/* Find first occurrence of any of the needles in tvbuff, starting at offset.
+
+/* Find first occurrence of any of the pattern chars in tvbuff, starting at offset.
  * Searches at most maxlength number of bytes; if maxlength is -1, searches
  * to end of tvbuff.
  * Returns the offset of the found needle, or -1 if not found.
@@ -1529,28 +1917,22 @@ tvb_pbrk_guint8_generic(tvbuff_t *tvb, guint abs_offset, guint limit, const guin
  * in that case, -1 will be returned if the boundary is reached before
  * finding needle. */
 gint
-tvb_pbrk_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const guint8 *needles, guchar *found_needle)
+tvb_ws_mempbrk_pattern_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength,
+                       const ws_mempbrk_pattern* pattern, guchar *found_needle)
 {
        const guint8 *result;
-       guint         abs_offset;
-       guint         tvbufflen;
-       guint         limit;
+       guint         abs_offset = 0;
+       guint         limit = 0;
+       int           exception;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
-       check_offset_length(tvb, offset, -1, &abs_offset, &tvbufflen);
+       exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
+       if (exception)
+               THROW(exception);
 
        /* Only search to end of tvbuff, w/o throwing exception. */
-       if (maxlength == -1) {
-               /* No maximum length specified; search to end of tvbuff. */
-               limit = tvbufflen;
-       }
-       else if (tvbufflen < (guint) maxlength) {
-               /* Maximum length goes past end of tvbuff; search to end
-                  of tvbuff. */
-               limit = tvbufflen;
-       }
-       else {
+       if (limit > (guint) maxlength) {
                /* Maximum length doesn't go past end of tvbuff; search
                   to that value. */
                limit = maxlength;
@@ -1558,7 +1940,7 @@ tvb_pbrk_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const gu
 
        /* If we have real data, perform our search now. */
        if (tvb->real_data) {
-               result = guint8_pbrk(tvb->real_data + abs_offset, limit, needles, found_needle);
+               result = ws_mempbrk_exec(tvb->real_data + abs_offset, limit, pattern, found_needle);
                if (result == NULL) {
                        return -1;
                }
@@ -1567,10 +1949,10 @@ tvb_pbrk_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const gu
                }
        }
 
-       if (tvb->ops->tvb_pbrk_guint8)
-               return tvb->ops->tvb_pbrk_guint8(tvb, abs_offset, limit, needles, found_needle);
+       if (tvb->ops->tvb_ws_mempbrk_pattern_guint8)
+               return tvb->ops->tvb_ws_mempbrk_pattern_guint8(tvb, abs_offset, limit, pattern, found_needle);
 
-       return tvb_pbrk_guint8_generic(tvb, abs_offset, limit, needles, found_needle);
+       return tvb_ws_mempbrk_guint8_generic(tvb, abs_offset, limit, pattern, found_needle);
 }
 
 /* Find size of stringz (NUL-terminated string) by looking for terminating
@@ -1581,7 +1963,7 @@ tvb_pbrk_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const gu
 guint
 tvb_strsize(tvbuff_t *tvb, const gint offset)
 {
-       guint abs_offset, junk_length;
+       guint abs_offset = 0, junk_length;
        gint  nul_offset;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
@@ -1639,7 +2021,7 @@ gint
 tvb_strnlen(tvbuff_t *tvb, const gint offset, const guint maxlength)
 {
        gint  result_offset;
-       guint abs_offset, junk_length;
+       guint abs_offset = 0, junk_length;
 
        DISSECTOR_ASSERT(tvb && tvb->initialized);
 
@@ -1740,43 +2122,6 @@ tvb_memeql(tvbuff_t *tvb, const gint offset, const guint8 *str, size_t size)
        }
 }
 
-/* Convert a string from Unicode to ASCII.     At the moment we fake it by
- * replacing all non-ASCII characters with a '.' )-:   The len parameter is
- * the number of guint16's to convert from Unicode.
- *
- * If scope is set to NULL, returned buffer is allocated by g_malloc()
- * and must be g_free by the caller. Otherwise memory is automatically
- * freed when the scope lifetime is reached.
- */
-/* XXX: This has been replaced by tvb_get_string() */
-char *
-tvb_get_faked_unicode(wmem_allocator_t *scope, tvbuff_t *tvb, int offset,
-                     const int len, const gboolean little_endian)
-{
-       char    *buffer;
-       int      i;
-       guint16  character;
-
-       /* Make sure we have enough data before allocating the buffer,
-          so we don't blow up if the length is huge. */
-       tvb_ensure_bytes_exist(tvb, offset, 2*len);
-
-       /* We know we won't throw an exception, so we don't have to worry
-          about leaking this buffer. */
-       buffer = (char *)wmem_alloc(scope, len + 1);
-
-       for (i = 0; i < len; i++) {
-               character = little_endian ? tvb_get_letohs(tvb, offset)
-                                         : tvb_get_ntohs(tvb, offset);
-               buffer[i] = character < 256 ? character : '.';
-               offset += 2;
-       }
-
-       buffer[len] = 0;
-
-       return buffer;
-}
-
 /*
  * Format the data in the tvb from offset for length ...
  */
@@ -1845,21 +2190,51 @@ tvb_format_stringzpad_wsp(tvbuff_t *tvb, const gint offset, const gint size)
        return format_text_wsp(ptr, stringlen);
 }
 
+/* Unicode REPLACEMENT CHARACTER */
+#define UNREPL 0x00FFFD
+
 /*
- * Given a tvbuff, an offset, and a length, allocate a buffer big enough
- * to hold a non-null-terminated string of that length at that offset,
- * plus a trailing '\0', copy the string into it, and return a pointer
- * to the string.
+ * All string functions below take a scope as an argument.
+ *
+ *
  * If scope is NULL, memory is allocated with g_malloc() and user must
  * explicitly free it with g_free().
  * If scope is not NULL, memory is allocated with the corresponding pool
  * lifetime.
- * Throws an exception if the tvbuff ends before the string does.
+ *
+ * All functions throw an exception if the tvbuff ends before the string
+ * does.
  */
-guint8 *
-tvb_get_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length)
+
+/*
+ * Given a wmem scope, tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, offset, and length as an ASCII string,
+ * with all bytes with the high-order bit set being invalid, and return a
+ * pointer to a UTF-8 string, allocated using the wmem scope.
+ *
+ * Octets with the highest bit set will be converted to the Unicode
+ * REPLACEMENT CHARACTER.
+ */
+static guint8 *
+tvb_get_ascii_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
 {
-       guint8       *strbuf;
+       const guint8  *ptr;
+
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_ascii_string(scope, ptr, length);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, the offset. and the length as a UTF-8
+ * string, and return a pointer to that string, allocated using the wmem scope.
+ *
+ * XXX - should map invalid UTF-8 sequences to UNREPL.
+ */
+static guint8 *
+tvb_get_utf_8_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length)
+{
+       guint8 *strbuf;
 
        tvb_ensure_bytes_exist(tvb, offset, length); /* make sure length = -1 fails */
        strbuf = (guint8 *)wmem_alloc(scope, length + 1);
@@ -1868,284 +2243,188 @@ tvb_get_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const
        return strbuf;
 }
 
-static guint8 *
-tvb_get_string_8859_1(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+/*
+ * Given a wmem scope, tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, the offset, and the length as a
+ * raw string, and return a pointer to that string, allocated using the
+ * wmem scope. This means a null is appended at the end, but no replacement
+ * checking is done otherwise. Currently tvb_get_utf_8_string() does not
+ * replace either, but it might in the future.
+ *
+ * Also, this one allows a length of -1 to mean get all, but does not
+ * allow a negative offset.
+ */
+static inline guint8 *
+tvb_get_raw_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length)
 {
-       wmem_strbuf_t *str;
+       guint8 *strbuf;
+       gint    abs_length = length;
 
-       str = wmem_strbuf_new(scope, "");
+       DISSECTOR_ASSERT(offset     >=  0);
+       DISSECTOR_ASSERT(abs_length >= -1);
 
-       while (length > 0) {
-               guint8 ch = tvb_get_guint8(tvb, offset);
+       if (abs_length < 0)
+               abs_length = tvb->length - offset;
 
-               if (ch < 0x80)
-                       wmem_strbuf_append_c(str, ch);
-               else {
-                       /*
-                        * Note: we assume here that the code points
-                        * 0x80-0x9F are used for C1 control characters,
-                        * and thus have the same value as the corresponding
-                        * Unicode code points.
-                        */
-                       wmem_strbuf_append_unichar(str, ch);
-               }
-               offset++;
-               length--;
-       }
-
-       /* XXX, discarding constiness, should we have some function which "take-over" strbuf->str (like when strbuf is no longer needed) */
-       return (guint8 *) wmem_strbuf_get_str(str);
+       tvb_ensure_bytes_exist(tvb, offset, abs_length);
+       strbuf = (guint8 *)wmem_alloc(scope, abs_length + 1);
+       tvb_memcpy(tvb, strbuf, offset, abs_length);
+       strbuf[abs_length] = '\0';
+       return strbuf;
 }
 
 /*
- * Given a string encoded using octet per character, with octets with
- * the high-order bit clear being ASCII, and a translation table that
- * maps values for other octets to 2-byte Unicode Basic Multilingual
- * Plane characters (including REPLACEMENT CHARACTER), return a UTF-8
- * string with the same characters.
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, the offset, and the length as an
+ * ISO 8859/1 string, and return a pointer to a UTF-8 string, allocated
+ * using the wmem scope.
  */
 static guint8 *
-tvb_get_string_unichar2(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length, const gunichar2 table[0x80])
+tvb_get_string_8859_1(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
 {
-       wmem_strbuf_t *str;
-
-       str = wmem_strbuf_new(scope, "");
+       const guint8  *ptr;
 
-       while (length > 0) {
-               guint8 ch = tvb_get_guint8(tvb, offset);
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_8859_1_string(scope, ptr, length);
+}
 
-               if (ch < 0x80)
-                       wmem_strbuf_append_c(str, ch);
-               else
-                       wmem_strbuf_append_unichar(str, table[ch-0x80]);
-               offset++;
-               length--;
-       }
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, and a translation
+ * table, treat the string of bytes referred to by the tvbuff, the offset,
+ * and the length as a string encoded using one octet per character, with
+ * octets with the high-order bit clear being ASCII and octets with the
+ * high-order bit set being mapped by the translation table to 2-byte
+ * Unicode Basic Multilingual Plane characters (including REPLACEMENT
+ * CHARACTER), and return a pointer to a UTF-8 string, allocated with the
+ * wmem scope.
+ */
+static guint8 *
+tvb_get_string_unichar2(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length, const gunichar2 table[0x80])
+{
+       const guint8  *ptr;
 
-       /* XXX, discarding constiness, should we have some function which "take-over" strbuf->str (like when strbuf is no longer needed) */
-       return (guint8 *) wmem_strbuf_get_str(str);
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_unichar2_string(scope, ptr, length, table);
 }
 
 /*
- * Given a UCS-2 encoded string containing characters from the
- * Basic Multilingual Plane (plane 0) of Unicode, return a UTF-8
- * string with the same characters.
- *
- * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN
+ * Given a wmem scope, a tvbuff, an offset, a length, and an encoding
+ * giving the byte order, treat the string of bytes referred to by the
+ * tvbuff, the offset, and the length as a UCS-2 encoded string in
+ * the byte order in question, containing characters from the Basic
+ * Multilingual Plane (plane 0) of Unicode, and return a pointer to a
+ * UTF-8 string, allocated with the wmem scope.
  *
- * Specify length in bytes
+ * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
  *
- * If scope is NULL, memory is allocated with g_malloc() and user must
- * explicitely free it with g_free().
- * If scope is not NULL, memory is allocated with the corresponding pool
- * lifetime.
+ * Specify length in bytes.
  *
  * XXX - should map lead and trail surrogate values to REPLACEMENT
  * CHARACTERs (0xFFFD)?
  * XXX - if there are an odd number of bytes, should put a
  * REPLACEMENT CHARACTER at the end.
  */
-static wmem_strbuf_t *
-tvb_extract_ucs_2_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
-{
-       gunichar2      uchar;
-       gint           i;       /* Byte counter for tvbuff */
-       wmem_strbuf_t *strbuf;
-
-       strbuf = wmem_strbuf_new(scope, NULL);
-
-       for(i = 0; i + 1 < length; i += 2) {
-               if (encoding == ENC_BIG_ENDIAN)
-                       uchar = tvb_get_ntohs(tvb, offset + i);
-               else
-                       uchar = tvb_get_letohs(tvb, offset + i);
-
-               wmem_strbuf_append_unichar(strbuf, uchar);
-       }
-
-       /*
-        * XXX - if i < length, this means we were handed an odd
-        * number of bytes, so we're not a valid UCS-2 string.
-        */
-       return strbuf;
-}
-
-static gchar *
+static guint8 *
 tvb_get_ucs_2_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
 {
-       wmem_strbuf_t *strbuf;
+       const guint8  *ptr;
 
-       tvb_ensure_bytes_exist(tvb, offset, length);
-       strbuf = tvb_extract_ucs_2_string(scope, tvb, offset, length, encoding);
-       return (gchar*)wmem_strbuf_get_str(strbuf);
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_ucs_2_string(scope, ptr, length, encoding);
 }
 
 /*
- * Given a UTF-16 encoded Unicode string, return a UTF-8 string with the
- * same characters.
- *
- * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN
+ * Given a wmem scope, a tvbuff, an offset, a length, and an encoding
+ * giving the byte order, treat the string of bytes referred to by the
+ * tvbuff, the offset, and the length as a UTF-16 encoded string in
+ * the byte order in question, and return a pointer to a UTF-8 string,
+ * allocated with the wmem scope.
  *
- * Specify length in bytes
+ * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
  *
- * If scope is NULL, memory is allocated with g_malloc() and user must
- * explicitely free it with g_free().
- * If scope is not NULL, memory is allocated with the corresponding pool
- * lifetime.
+ * Specify length in bytes.
  *
  * XXX - should map surrogate errors to REPLACEMENT CHARACTERs (0xFFFD).
  * XXX - should map code points > 10FFFF to REPLACEMENT CHARACTERs.
  * XXX - if there are an odd number of bytes, should put a
  * REPLACEMENT CHARACTER at the end.
  */
-
-#define IS_LEAD_SURROGATE(uchar2) \
-       ((uchar2) >= 0xd800 && (uchar2) < 0xdc00)
-#define IS_TRAIL_SURROGATE(uchar2) \
-       ((uchar2) >= 0xdc00 && (uchar2) < 0xe000)
-#define SURROGATE_VALUE(lead, trail) \
-       (((((lead) - 0xd800) << 10) + ((trail) - 0xdc00)) + 0x100000)
-
-static wmem_strbuf_t *
-tvb_extract_utf_16_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint size, const guint encoding)
-{
-       wmem_strbuf_t *strbuf;
-       gunichar2      uchar2, lead_surrogate;
-       gunichar       uchar;
-       gint           i;       /* Byte counter for tvbuff */
-
-       strbuf = wmem_strbuf_new(scope, NULL);
-
-       for(i = 0; i + 1 < size; i += 2) {
-               if (encoding == ENC_BIG_ENDIAN)
-                       uchar2 = tvb_get_ntohs(tvb, offset + i);
-               else
-                       uchar2 = tvb_get_letohs(tvb, offset + i);
-
-               if (IS_LEAD_SURROGATE(uchar2)) {
-                       /*
-                        * Lead surrogate.  Must be followed by
-                        * a trail surrogate.
-                        */
-                       i += 2;
-                       if (i + 1 >= size) {
-                               /*
-                                * Oops, string ends with a lead surrogate.
-                                * Ignore this for now.
-                                * XXX - insert "substitute" character?
-                                * Report the error in some other
-                                * fashion?
-                                */
-                               break;
-                       }
-                       lead_surrogate = uchar2;
-                       if (encoding == ENC_BIG_ENDIAN)
-                               uchar2 = tvb_get_ntohs(tvb, offset + i);
-                       else
-                               uchar2 = tvb_get_letohs(tvb, offset + i);
-                       if (IS_TRAIL_SURROGATE(uchar2)) {
-                               /* Trail surrogate. */
-                               uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
-                               wmem_strbuf_append_unichar(strbuf, uchar);
-                       } else {
-                               /*
-                                * Not a trail surrogate.
-                                * Ignore the entire pair. 
-                                * XXX - insert "substitute" character?
-                                * Report the error in some other
-                                * fashion?
-                                */
-                                ;
-                       }
-               } else {
-                       if (IS_TRAIL_SURROGATE(uchar2)) {
-                               /*
-                                * Trail surrogate without a preceding
-                                * lead surrogate.  Ignore it.
-                                * XXX - insert "substitute" character?
-                                * Report the error in some other
-                                * fashion?
-                                */
-                               ;
-                       } else {
-                               /*
-                                * Non-surrogate; just append it.
-                                */
-                               wmem_strbuf_append_unichar(strbuf, uchar2);
-                       }
-               }
-       }
-
-       /*
-        * XXX - if i < length, this means we were handed an odd
-        * number of bytes, so we're not a valid UTF-16 string.
-        */
-       return strbuf;
-}
-
-static gchar *
+static guint8 *
 tvb_get_utf_16_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
 {
-       wmem_strbuf_t *strbuf;
+       const guint8  *ptr;
 
-       tvb_ensure_bytes_exist(tvb, offset, length);
-       strbuf = tvb_extract_utf_16_string(scope, tvb, offset, length, encoding);
-       return (gchar*)wmem_strbuf_get_str(strbuf);
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_utf_16_string(scope, ptr, length, encoding);
 }
 
 /*
- * Given a UCS-4-encoded Unicode string, return a UTF-8 string with the
- * same characters.
+ * Given a wmem scope, a tvbuff, an offset, a length, and an encoding
+ * giving the byte order, treat the string of bytes referred to by the
+ * tvbuff, the offset, and the length as a UCS-4 encoded string in
+ * the byte order in question, and return a pointer to a UTF-8 string,
+ * allocated with the wmem scope.
  *
  * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN
  *
  * Specify length in bytes
  *
- * If scope is NULL, memory is allocated with g_malloc() and user must
- * explicitely free it with g_free().
- * If scope is not NULL, memory is allocated with the corresponding pool
- * lifetime.
- *
  * XXX - should map lead and trail surrogate values to a "substitute"
  * UTF-8 character?
  * XXX - should map code points > 10FFFF to REPLACEMENT CHARACTERs.
  * XXX - if the number of bytes isn't a multiple of 4, should put a
  * REPLACEMENT CHARACTER at the end.
  */
-static wmem_strbuf_t *
-tvb_extract_ucs_4_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
+static gchar *
+tvb_get_ucs_4_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
 {
-       gunichar       uchar;
-       gint           i;       /* Byte counter for tvbuff */
-       wmem_strbuf_t *strbuf;
+       const guint8 *ptr;
 
-       strbuf = wmem_strbuf_new(scope, NULL);
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_ucs_4_string(scope, ptr, length, encoding);
+}
 
-       for(i = 0; i + 3 < length; i += 4) {
-               if (encoding == ENC_BIG_ENDIAN)
-                       uchar = tvb_get_ntohl(tvb, offset + i);
-               else
-                       uchar = tvb_get_letohl(tvb, offset + i);
+gchar *
+tvb_get_ts_23_038_7bits_string(wmem_allocator_t *scope, tvbuff_t *tvb,
+       const gint bit_offset, gint no_of_chars)
+{
+       gint           in_offset = bit_offset >> 3; /* Current pointer to the input buffer */
+       gint           length = ((no_of_chars + 1) * 7 + (bit_offset & 0x07)) >> 3;
+       const guint8  *ptr;
 
-               wmem_strbuf_append_unichar(strbuf, uchar);
-       }
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
 
-       /*
-        * XXX - if i < length, this means we were handed a number
-        * of bytes that's not a multiple of 4, so we're not a valid
-        * UCS-4 string.
-        */
-       return strbuf;
+       ptr = ensure_contiguous(tvb, in_offset, length);
+       return get_ts_23_038_7bits_string(scope, ptr, bit_offset, no_of_chars);
 }
 
-static gchar *
-tvb_get_ucs_4_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
+gchar *
+tvb_get_ascii_7bits_string(wmem_allocator_t *scope, tvbuff_t *tvb,
+       const gint bit_offset, gint no_of_chars)
 {
-       wmem_strbuf_t *strbuf;
+       gint           in_offset = bit_offset >> 3; /* Current pointer to the input buffer */
+       gint           length = ((no_of_chars + 1) * 7 + (bit_offset & 0x07)) >> 3;
+       const guint8  *ptr;
+
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
 
-       tvb_ensure_bytes_exist(tvb, offset, length);
-       strbuf = tvb_extract_ucs_4_string(scope, tvb, offset, length, encoding);
-       return (gchar*)wmem_strbuf_get_str(strbuf);
+       ptr = ensure_contiguous(tvb, in_offset, length);
+       return get_ascii_7bits_string(scope, ptr, bit_offset, no_of_chars);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, offset, and length as a string encoded
+ * in EBCDIC using one octet per character, and return a pointer to a
+ * UTF-8 string, allocated using the wmem scope.
+ */
+static guint8 *
+tvb_get_ebcdic_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+       const guint8  *ptr;
+
+       ptr = ensure_contiguous(tvb, offset, length);
+       return get_ebcdic_string(scope, ptr, length);
 }
 
 /*
@@ -2154,20 +2433,19 @@ tvb_get_ucs_4_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
  * at that offset, plus a trailing '\0', copy into the buffer the
  * string as converted from the appropriate encoding to UTF-8, and
  * return a pointer to the string.
- *
- * Throws an exception if the tvbuff ends before the string does.
- *
- * If scope is NULL, memory is allocated with g_malloc() and user must
- * explicitely free it with g_free().
- * If scope is not NULL, memory is allocated with the corresponding pool
- * lifetime.
  */
 guint8 *
 tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
                             const gint length, const guint encoding)
 {
-       const guint8 *ptr;
-       guint8       *strbuf;
+       guint8 *strptr;
+
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+       /* make sure length = -1 fails */
+       if (length < 0) {
+               THROW(ReportedBoundsError);
+       }
 
        switch (encoding & ENC_CHARENCODING_MASK) {
 
@@ -2181,11 +2459,8 @@ tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
                 * was a gboolean for the byte order, not an
                 * encoding value, and passed non-zero values
                 * other than TRUE to mean "little-endian".
-                *
-                * XXX - should map all octets with the 8th bit
-                * set REPLACEMENT CHARACTERs.
                 */
-               strbuf = tvb_get_string(scope, tvb, offset, length);
+               strptr = tvb_get_ascii_string(scope, tvb, offset, length);
                break;
 
        case ENC_UTF_8:
@@ -2194,24 +2469,22 @@ tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
                 * points to a "substitute" UTF-8 character?
                 * XXX - should map code points > 10FFFF to REPLACEMENT
                 * CHARACTERs.
-                * XXX - should map invalid UTF-8 sequences to
-                * REPLACEMENT CHARACTERs.
                 */
-               strbuf = tvb_get_string(scope, tvb, offset, length);
+               strptr = tvb_get_utf_8_string(scope, tvb, offset, length);
                break;
 
        case ENC_UTF_16:
-               strbuf = tvb_get_utf_16_string(scope, tvb, offset, length,
+               strptr = tvb_get_utf_16_string(scope, tvb, offset, length,
                    encoding & ENC_LITTLE_ENDIAN);
                break;
 
        case ENC_UCS_2:
-               strbuf = tvb_get_ucs_2_string(scope, tvb, offset, length,
+               strptr = tvb_get_ucs_2_string(scope, tvb, offset, length,
                    encoding & ENC_LITTLE_ENDIAN);
                break;
 
        case ENC_UCS_4:
-               strbuf = tvb_get_ucs_4_string(scope, tvb, offset, length,
+               strptr = tvb_get_ucs_4_string(scope, tvb, offset, length,
                    encoding & ENC_LITTLE_ENDIAN);
                break;
 
@@ -2221,103 +2494,146 @@ tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
                 * to the equivalent Unicode code point value, so
                 * no translation table is needed.
                 */
-               strbuf = tvb_get_string_8859_1(scope, tvb, offset, length);
+               strptr = tvb_get_string_8859_1(scope, tvb, offset, length);
                break;
 
        case ENC_ISO_8859_2:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_2);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_2);
                break;
 
        case ENC_ISO_8859_3:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_3);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_3);
                break;
 
        case ENC_ISO_8859_4:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_4);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_4);
                break;
 
        case ENC_ISO_8859_5:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_5);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_5);
                break;
 
        case ENC_ISO_8859_6:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_6);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_6);
                break;
 
        case ENC_ISO_8859_7:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_7);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_7);
                break;
 
        case ENC_ISO_8859_8:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_8);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_8);
                break;
 
        case ENC_ISO_8859_9:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_9);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_9);
                break;
 
        case ENC_ISO_8859_10:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_10);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_10);
                break;
 
        case ENC_ISO_8859_11:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_11);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_11);
                break;
 
        case ENC_ISO_8859_13:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_13);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_13);
                break;
 
        case ENC_ISO_8859_14:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_14);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_14);
                break;
 
        case ENC_ISO_8859_15:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_15);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_15);
                break;
 
        case ENC_ISO_8859_16:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_16);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_16);
                break;
 
        case ENC_WINDOWS_1250:
-               strbuf = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1250);
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1250);
+               break;
+
+       case ENC_MAC_ROMAN:
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_mac_roman);
+               break;
+
+       case ENC_CP437:
+               strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp437);
+               break;
+
+       case ENC_3GPP_TS_23_038_7BITS:
+               {
+                       gint bit_offset  = offset << 3;
+                       gint no_of_chars = (length << 3) / 7;
+                       strptr = tvb_get_ts_23_038_7bits_string(scope, tvb, bit_offset, no_of_chars);
+               }
+               break;
+
+       case ENC_ASCII_7BITS:
+               {
+                       gint bit_offset  = offset << 3;
+                       gint no_of_chars = (length << 3) / 7;
+                       strptr = tvb_get_ascii_7bits_string(scope, tvb, bit_offset, no_of_chars);
+               }
                break;
 
        case ENC_EBCDIC:
                /*
-                * XXX - do the copy and conversion in one pass.
-                *
                 * XXX - multiple "dialects" of EBCDIC?
                 */
-               tvb_ensure_bytes_exist(tvb, offset, length); /* make sure length = -1 fails */
-               strbuf = (guint8 *)wmem_alloc(scope, length + 1);
-               if (length != 0) {
-                       ptr = ensure_contiguous(tvb, offset, length);
-                       memcpy(strbuf, ptr, length);
-                       EBCDIC_to_ASCII(strbuf, length);
-               }
-               strbuf[length] = '\0';
+               strptr = tvb_get_ebcdic_string(scope, tvb, offset, length);
                break;
        }
-       return strbuf;
+       return strptr;
 }
 
 /*
- * Given a tvbuff and an offset, with the offset assumed to refer to
- * a null-terminated string, find the length of that string (and throw
- * an exception if the tvbuff ends before we find the null), allocate
- * a buffer big enough to hold the string, copy the string into it,
- * and return a pointer to the string. Also return the length of the
- * string (including the terminating null) through a pointer.
+ * This is like tvb_get_string_enc(), except that it handles null-padded
+ * strings.
  *
- * If scope is NULL, memory is allocated with g_malloc() and user must
- * explicitely free it with g_free().
- * If scope is not NULL, memory is allocated with the corresponding pool
- * lifetime.
+ * Currently, string values are stored as UTF-8 null-terminated strings,
+ * so nothing needs to be done differently for null-padded strings; we
+ * could save a little memory by not storing the null padding.
+ *
+ * If we ever store string values differently, in a fashion that doesn't
+ * involve null termination, that might change.
  */
 guint8 *
-tvb_get_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp)
+tvb_get_stringzpad(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
+                  const gint length, const guint encoding)
+{
+       return tvb_get_string_enc(scope, tvb, offset, length, encoding);
+}
+
+/*
+ * These routines are like the above routines, except that they handle
+ * null-terminated strings.  They find the length of that string (and
+ * throw an exception if the tvbuff ends before we find the null), and
+ * also return through a pointer the length of the string, in bytes,
+ * including the terminating null (the terminating null being 2 bytes
+ * for UCS-2 and UTF-16, 4 bytes for UCS-4, and 1 byte for other
+ * encodings).
+ */
+static guint8 *
+tvb_get_ascii_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+       guint          size;
+       const guint8  *ptr;
+
+       size = tvb_strsize(tvb, offset);
+       ptr  = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
+       if (lengthp)
+               *lengthp = size;
+       return get_ascii_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_utf_8_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp)
 {
        guint   size;
        guint8 *strptr;
@@ -2334,22 +2650,28 @@ static guint8 *
 tvb_get_stringz_8859_1(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
 {
        guint size;
+       const guint8  *ptr;
 
-       /* XXX, convertion between signed/unsigned integer */
-       *lengthp = size = tvb_strsize(tvb, offset);
-
-       return tvb_get_string_8859_1(scope, tvb, offset, size);
+       size = tvb_strsize(tvb, offset);
+       ptr = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
+       if (lengthp)
+               *lengthp = size;
+       return get_8859_1_string(scope, ptr, size);
 }
 
 static guint8 *
 tvb_get_stringz_unichar2(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp, const gunichar2 table[0x80])
 {
        guint size;
+       const guint8  *ptr;
 
-       /* XXX, convertion between signed/unsigned integer */
-       *lengthp = size = tvb_strsize(tvb, offset);
-
-       return tvb_get_string_unichar2(scope, tvb, offset, size, table);
+       size = tvb_strsize(tvb, offset);
+       ptr = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
+       if (lengthp)
+               *lengthp = size;
+       return get_unichar2_string(scope, ptr, size, table);
 }
 
 /*
@@ -2363,7 +2685,7 @@ tvb_get_stringz_unichar2(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gi
  * As long as we aren't using composite TVBs, this saves the cycles used
  * (often unnecessariliy) in allocating a buffer and copying the string into
  * it.  (If we do start using composite TVBs, we may want to replace this
- * function with the _ephemeral versoin.)
+ * function with the _ephemeral version.)
  */
 const guint8 *
 tvb_get_const_stringz(tvbuff_t *tvb, const gint offset, gint *lengthp)
@@ -2378,71 +2700,40 @@ tvb_get_const_stringz(tvbuff_t *tvb, const gint offset, gint *lengthp)
        return strptr;
 }
 
-/*
- * Version of tvb_get_stringz() that handles the Basic Multilingual Plane
- * (plane 0) of Unicode, with each code point encoded in 16 bits.
- *
- * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
- *
- * Returns an allocated UTF-8 string and updates lengthp pointer with
- * length of string (in bytes), including the terminating (2-byte) NUL.
- */
 static gchar *
 tvb_get_ucs_2_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
 {
        gint           size;    /* Number of bytes in string */
-       wmem_strbuf_t *strbuf;
+       const guint8  *ptr;
 
        size = tvb_unicode_strsize(tvb, offset);
-
-       strbuf = tvb_extract_ucs_2_string(scope, tvb, offset, size, encoding);
-
+       ptr = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
        if (lengthp)
                *lengthp = size;
-
-       return (gchar*)wmem_strbuf_get_str(strbuf);
+       return get_ucs_2_string(scope, ptr, size, encoding);
 }
 
-/*
- * Version of tvb_get_stringz() that handles UTF-16.
- *
- * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
- *
- * Returns an allocated UTF-8 string and updates lengthp pointer with
- * length of string (in bytes), including the terminating (2-byte) NUL.
- */
 static gchar *
 tvb_get_utf_16_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
 {
        gint           size;
-       wmem_strbuf_t *strbuf;
+       const guint8  *ptr;
 
        size = tvb_unicode_strsize(tvb, offset);
-
-       strbuf = tvb_extract_utf_16_string(scope, tvb, offset, size, encoding);
-
+       ptr = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
        if (lengthp)
                *lengthp = size;
-
-       return (gchar*)wmem_strbuf_get_str(strbuf);
+       return get_utf_16_string(scope, ptr, size, encoding);
 }
 
-/*
- * Version of tvb_get_stringz() that handles UCS-4.
- *
- * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
- *
- * Returns an allocated UTF-8 string and updates lengthp pointer with
- * length of string (in bytes), including the terminating (4-byte) NUL.
- */
 static gchar *
 tvb_get_ucs_4_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
 {
+       gint           size;
        gunichar       uchar;
-       gint           size;    /* Number of bytes in string */
-       wmem_strbuf_t *strbuf;
-
-       DISSECTOR_ASSERT(tvb && tvb->initialized);
+       const guint8  *ptr;
 
        size = 0;
        do {
@@ -2451,20 +2742,34 @@ tvb_get_ucs_4_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
                size += 4;
        } while(uchar != 0);
 
-       strbuf = tvb_extract_ucs_4_string(scope, tvb, offset, size, encoding);
-
+       ptr = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
        if (lengthp)
-               *lengthp = size; /* Number of *bytes* processed */
+               *lengthp = size;
+       return get_ucs_4_string(scope, ptr, size, encoding);
+}
 
-       return (gchar*)wmem_strbuf_get_str(strbuf);
+static guint8 *
+tvb_get_ebcdic_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+       guint          size;
+       const guint8  *ptr;
+
+       size = tvb_strsize(tvb, offset);
+       ptr  = ensure_contiguous(tvb, offset, size);
+       /* XXX, conversion between signed/unsigned integer */
+       if (lengthp)
+               *lengthp = size;
+       return get_ebcdic_string(scope, ptr, size);
 }
 
 guint8 *
 tvb_get_stringz_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
 {
-       guint   size;
        guint8 *strptr;
 
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
+
        switch (encoding & ENC_CHARENCODING_MASK) {
 
        case ENC_ASCII:
@@ -2477,19 +2782,18 @@ tvb_get_stringz_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, g
                 * was a gboolean for the byte order, not an
                 * encoding value, and passed non-zero values
                 * other than TRUE to mean "little-endian".
-                *
-                * XXX - should map all octets with the 8th bit
-                * not set to a "substitute" UTF-8 character.
                 */
-               strptr = tvb_get_stringz(scope, tvb, offset, lengthp);
+               strptr = tvb_get_ascii_stringz(scope, tvb, offset, lengthp);
                break;
 
        case ENC_UTF_8:
                /*
                 * XXX - should map all invalid UTF-8 sequences
                 * to a "substitute" UTF-8 character.
+                * XXX - should map code points > 10FFFF to REPLACEMENT
+                * CHARACTERs.
                 */
-               strptr = tvb_get_stringz(scope, tvb, offset, lengthp);
+               strptr = tvb_get_utf_8_stringz(scope, tvb, offset, lengthp);
                break;
 
        case ENC_UTF_16:
@@ -2576,18 +2880,27 @@ tvb_get_stringz_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, g
                strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1250);
                break;
 
+       case ENC_MAC_ROMAN:
+               strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_mac_roman);
+               break;
+
+       case ENC_CP437:
+               strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp437);
+               break;
+
+       case ENC_3GPP_TS_23_038_7BITS:
+               REPORT_DISSECTOR_BUG("TS 23.038 7bits has no null character and doesn't support null-terminated strings");
+               break;
+
+       case ENC_ASCII_7BITS:
+               REPORT_DISSECTOR_BUG("tvb_get_stringz_enc function with ENC_ASCII_7BITS not implemented yet");
+               break;
+
        case ENC_EBCDIC:
                /*
-                * XXX - do the copy and conversion in one pass.
-                *
                 * XXX - multiple "dialects" of EBCDIC?
                 */
-               size = tvb_strsize(tvb, offset);
-               strptr = (guint8 *)wmem_alloc(scope, size);
-               tvb_memcpy(tvb, strptr, offset, size);
-               EBCDIC_to_ASCII(strptr, size);
-               if (lengthp)
-                       *lengthp = size;
+               strptr = tvb_get_ebcdic_stringz(scope, tvb, offset, lengthp);
                break;
        }
 
@@ -2616,8 +2929,8 @@ static gint
 _tvb_get_nstringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8* buffer, gint *bytes_copied)
 {
        gint     stringlen;
-       guint    abs_offset;
-       gint     limit, len;
+       guint    abs_offset = 0;
+       gint     limit, len = 0;
        gboolean decreased_max = FALSE;
 
        /* Only read to end of tvbuff, w/o throwing exception. */
@@ -2694,7 +3007,7 @@ _tvb_get_nstringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8*
  * at the correct spot, terminating the string.
  */
 gint
-tvb_get_nstringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8buffer)
+tvb_get_nstringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8 *buffer)
 {
        gint bytes_copied;
 
@@ -2725,6 +3038,8 @@ tvb_get_nstringz0(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8*
        }
 }
 
+
+static ws_mempbrk_pattern pbrk_crlf;
 /*
  * Given a tvbuff, an offset into the tvbuff, and a length that starts
  * at that offset (which may be -1 for "all the way to the end of the
@@ -2751,19 +3066,27 @@ tvb_find_line_end(tvbuff_t *tvb, const gint offset, int len, gint *next_offset,
        gint   eol_offset;
        int    linelen;
        guchar found_needle = 0;
+       static gboolean compiled = FALSE;
+
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
 
        if (len == -1)
-               len = tvb_length_remaining(tvb, offset);
+               len = _tvb_captured_length_remaining(tvb, offset);
        /*
         * XXX - what if "len" is still -1, meaning "offset is past the
         * end of the tvbuff"?
         */
        eob_offset = offset + len;
 
+       if (!compiled) {
+               ws_mempbrk_compile(&pbrk_crlf, "\r\n");
+               compiled = TRUE;
+       }
+
        /*
         * Look either for a CR or an LF.
         */
-       eol_offset = tvb_pbrk_guint8(tvb, offset, len, "\r\n", &found_needle);
+       eol_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, len, &pbrk_crlf, &found_needle);
        if (eol_offset == -1) {
                /*
                 * No CR or LF - line is presumably continued in next packet.
@@ -2841,6 +3164,7 @@ tvb_find_line_end(tvbuff_t *tvb, const gint offset, int len, gint *next_offset,
        return linelen;
 }
 
+static ws_mempbrk_pattern pbrk_crlf_dquote;
 /*
  * Given a tvbuff, an offset into the tvbuff, and a length that starts
  * at that offset (which may be -1 for "all the way to the end of the
@@ -2867,9 +3191,18 @@ tvb_find_line_end_unquoted(tvbuff_t *tvb, const gint offset, int len, gint *next
        guchar   c = 0;
        gint     eob_offset;
        int      linelen;
+       static gboolean compiled = FALSE;
+
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
 
        if (len == -1)
-               len = tvb_length_remaining(tvb, offset);
+               len = _tvb_captured_length_remaining(tvb, offset);
+
+       if (!compiled) {
+               ws_mempbrk_compile(&pbrk_crlf_dquote, "\r\n\"");
+               compiled = TRUE;
+       }
+
        /*
         * XXX - what if "len" is still -1, meaning "offset is past the
         * end of the tvbuff"?
@@ -2892,7 +3225,7 @@ tvb_find_line_end_unquoted(tvbuff_t *tvb, const gint offset, int len, gint *next
                        /*
                         * Look either for a CR, an LF, or a '"'.
                         */
-                       char_offset = tvb_pbrk_guint8(tvb, cur_offset, len, "\r\n\"", &c);
+                       char_offset = tvb_ws_mempbrk_pattern_guint8(tvb, cur_offset, len, &pbrk_crlf_dquote, &c);
                }
                if (char_offset == -1) {
                        /*
@@ -3006,8 +3339,12 @@ tvb_skip_wsp(tvbuff_t *tvb, const gint offset, const gint maxlength)
        gint   end, tvb_len;
        guint8 tempchar;
 
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
+
        /* Get the length remaining */
-       tvb_len = tvb_length(tvb);
+       /*tvb_len = tvb_captured_length(tvb);*/
+       tvb_len = tvb->length;
+
        end     = offset + maxlength;
        if (end >= tvb_len)
        {
@@ -3036,6 +3373,31 @@ tvb_skip_wsp_return(tvbuff_t *tvb, const gint offset) {
        return (counter);
 }
 
+int
+tvb_skip_guint8(tvbuff_t *tvb, int offset, const int maxlength, const guint8 ch)
+{
+       int end, tvb_len;
+
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+       /* Get the length remaining */
+       /*tvb_len = tvb_captured_length(tvb);*/
+       tvb_len = tvb->length;
+
+       end     = offset + maxlength;
+       if (end >= tvb_len)
+               end = tvb_len;
+
+       while (offset < end) {
+               guint8 tempch = tvb_get_guint8(tvb, offset);
+
+               if (tempch != ch)
+                       break;
+               offset++;
+       }
+
+       return offset;
+}
 
 /*
  * Format a bunch of data from a tvbuff as bytes, returning a pointer
@@ -3043,9 +3405,9 @@ tvb_skip_wsp_return(tvbuff_t *tvb, const gint offset) {
  * separator.
  */
 gchar *
-tvb_bytes_to_ep_str_punct(tvbuff_t *tvb, const gint offset, const gint len, const gchar punct)
+tvb_bytes_to_str_punct(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint len, const gchar punct)
 {
-       return bytes_to_ep_str_punct(ensure_contiguous(tvb, offset, len), len, punct);
+       return bytestring_to_str(scope, ensure_contiguous(tvb, offset, len), len, punct);
 }
 
 
@@ -3073,11 +3435,14 @@ tvb_bcd_dig_to_wmem_packet_str(tvbuff_t *tvb, const gint offset, const gint len,
        char   *digit_str;
        gint    t_offset = offset;
 
+       DISSECTOR_ASSERT(tvb && tvb->initialized);
+
        if (!dgt)
                dgt = &Dgt1_9_bcd;
 
        if (len == -1) {
-               length = tvb_length(tvb);
+               /*length = tvb_captured_length(tvb);*/
+               length = tvb->length;
                if (length < offset) {
                        return "";
                }
@@ -3100,8 +3465,15 @@ tvb_bcd_dig_to_wmem_packet_str(tvbuff_t *tvb, const gint offset, const gint len,
                 */
                octet = octet >> 4;
 
-               if (octet == 0x0f)      /* odd number bytes - hit filler */
+               if (t_offset == length - 1 && octet == 0x0f) {
+                       /*
+                        * This is the last octet, and the low-order
+                        * nibble is 0xf, so we have an odd number of
+                        * digits, and this is a filler digit.  Ignore
+                        * it.
+                        */
                        break;
+               }
 
                digit_str[i] = dgt->out[octet & 0x0f];
                i++;
@@ -3117,17 +3489,17 @@ tvb_bcd_dig_to_wmem_packet_str(tvbuff_t *tvb, const gint offset, const gint len,
  * Format a bunch of data from a tvbuff as bytes, returning a pointer
  * to the string with the formatted data.
  */
-gchar *
-tvb_bytes_to_ep_str(tvbuff_t *tvb, const gint offset, const gint len)
+gchar *tvb_bytes_to_str(wmem_allocator_t *allocator, tvbuff_t *tvb,
+    const gint offset, const gint len)
 {
-       return bytes_to_ep_str(ensure_contiguous(tvb, offset, len), len);
+       return bytes_to_str(allocator, ensure_contiguous(tvb, offset, len), len);
 }
 
 /* Find a needle tvbuff within a haystack tvbuff. */
 gint
 tvb_find_tvb(tvbuff_t *haystack_tvb, tvbuff_t *needle_tvb, const gint haystack_offset)
 {
-       guint         haystack_abs_offset, haystack_abs_length;
+       guint         haystack_abs_offset = 0, haystack_abs_length = 0;
        const guint8 *haystack_data;
        const guint8 *needle_data;
        const guint   needle_len = needle_tvb->length;