Quellcode durchsuchen

Added string builder ToNumber methods. (#390)

* Added string builder ToNumber methods.

Implemented more efficient strto number functions.

* Formatting and replaced SIZE_T_MAX with SIZE_MAX
Brucey vor 2 Wochen
Ursprung
Commit
b7867fd7a6

+ 1 - 0
blitz.mod/blitz.bmx

@@ -123,6 +123,7 @@ Import "blitz_gc.c"
 Import "blitz_unicode.c"
 Import "blitz_enum.c"
 Import "blitz_coverage.c"
+Import "blitz_strto.c"
 Import "blitz_string_ex.cpp"
 
 ?coverage

+ 1 - 0
blitz.mod/blitz.h

@@ -40,6 +40,7 @@
 #include "blitz_app.h" 
 #include "blitz_enum.h"
 #include "blitz_coverage.h"
+#include "blitz_strto.h"
 
 #ifdef __cplusplus
 extern "C"{

+ 3 - 0
blitz.mod/blitz_string.h

@@ -182,6 +182,9 @@ int	bbStringToSizeTEx( BBString *str, BBSIZET *val, int start, int end, BBULONG
 int	bbStringToLongIntEx( BBString *str, BBLONGINT *val, int start, int end, BBULONG format, int base );
 int	bbStringToULongIntEx( BBString *str, BBULONGINT *val, int start, int end, BBULONG format, int base );
 
+int bbStrToDoubleEx( BBChar *buf, int length, double * val, int startPos, int endPos, BBULONG format, BBString* sep );
+int bbStrToFloatEx( BBChar *buf, int length, float * val, int startPos, int endPos, BBULONG format, BBString* sep );
+
 BBUINT* bbStringToUTF32String( BBString *str );
 BBString* bbStringFromUTF32String( const BBUINT *p );
 BBString* bbStringFromUTF32Bytes( const BBUINT *p, int n );

+ 16 - 8
blitz.mod/blitz_string_ex.cpp

@@ -30,14 +30,18 @@ typedef LONG_PTR LPARAM;
 // endPos of -1 means the end of the string
 // returns 0 if the string is not a valid double, or the position of the first character after the double otherwise
 int bbStringToDoubleEx( BBString *str, double * val, int startPos, int endPos, BBULONG format, BBString* sep ) {
-    if ( startPos < 0 || startPos >= str->length || endPos < -1 || endPos > str->length ) {
+    return bbStrToDoubleEx( str->buf, str->length, val, startPos, endPos, format, sep );
+}
+
+int bbStrToDoubleEx( BBChar *buf, int length, double * val, int startPos, int endPos, BBULONG format, BBString* sep ) {
+    if ( startPos < 0 || startPos >= length || endPos < -1 || endPos > length ) {
         return 0;
     }
     if (endPos == -1) {
-        endPos = str->length;
+        endPos = length;
     }
-    const char16_t * start = (char16_t*)str->buf;
-    const char16_t * end = start + str->length;
+    const char16_t * start = (char16_t*)buf;
+    const char16_t * end = start + length;
     const char16_t * p = start + startPos;
     const char16_t * e = start + endPos;
     const char16_t sepChar = sep->length > 0 ? sep->buf[0] : '.';
@@ -65,14 +69,18 @@ int bbStringToDoubleEx( BBString *str, double * val, int startPos, int endPos, B
 // endPos of -1 means the end of the string
 // returns 0 if the string is not a valid float, or the position of the first character after the float otherwise
 int bbStringToFloatEx( BBString *str, float * val, int startPos, int endPos, BBULONG format, BBString* sep ) {
-    if ( startPos < 0 || startPos >= str->length || endPos < -1 || endPos > str->length ) {
+    return bbStrToFloatEx( str->buf, str->length, val, startPos, endPos, format, sep );
+}
+
+int bbStrToFloatEx( BBChar *buf, int length, float * val, int startPos, int endPos, BBULONG format, BBString* sep ) {
+    if ( startPos < 0 || startPos >= length || endPos < -1 || endPos > length ) {
         return 0;
     }
     if (endPos == -1) {
-        endPos = str->length;
+        endPos = length;
     }
-    const char16_t * start = (char16_t*)str->buf;
-    const char16_t * end = start + str->length;
+    const char16_t * start = (char16_t*)buf;
+    const char16_t * end = start + length;
     const char16_t * p = start + startPos;
     const char16_t * e = start + endPos;
     const char16_t sepChar = sep->length > 0 ? sep->buf[0] : '.';

+ 368 - 0
blitz.mod/blitz_strto.c

@@ -0,0 +1,368 @@
+
+#include "blitz.h"
+
+#include <limits.h>
+#include <errno.h>
+#include <stdint.h>
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#  define BB_INLINE inline
+#else
+#  define BB_INLINE /* nothing */
+#endif
+
+static BB_INLINE int bbIsspace(BBChar c) {
+    return c == 0x20 || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+static BB_INLINE int bbIsdigit(BBChar c) {
+    return c >= '0' && c <= '9';
+}
+
+/* Convert a BBChar to a digit value for the given base (2,10,16). 
+   Returns 0..base-1 on success, or -1 if not a valid digit. */
+static BB_INLINE int bbGetdigit(BBChar ch, int base) {
+    int v;
+    if (ch >= '0' && ch <= '9') {
+        v = (int)(ch - '0');
+    } else if (ch >= 'A' && ch <= 'F') {
+        v = 10 + (int)(ch - 'A');
+    } else if (ch >= 'a' && ch <= 'f') {
+        v = 10 + (int)(ch - 'a');
+    } else {
+        return -1;
+    }
+    return (v < base) ? v : -1;  /* reject e.g. '2' in base 2, 'A' in base 10 */
+}
+
+BBLONGINT bbStrToLongInt(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBLONG v = bbStrToLong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0; // no digits
+    }
+    if (errno == ERANGE) {
+        return 0; // overflow/underflow
+    }
+    if (v < LONG_MIN || v > LONG_MAX) {
+        errno = ERANGE;
+        return 0; // out of int range
+    }
+    return (BBLONGINT)v;
+}
+
+BBINT bbStrToInt(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBLONG v = bbStrToLong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0; // no digits
+    }
+    if (errno == ERANGE) {
+        return 0; // overflow/underflow
+    }
+    if (v < INT_MIN) {
+        errno = ERANGE;
+        return INT_MIN; // out of int range
+    } else if (v > INT_MAX) {
+        errno = ERANGE;
+        return INT_MAX; // out of int range
+    }
+    return (int)v;
+}
+
+BBULONGINT bbStrToULongInt(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBULONG v = bbStrToULong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0;  // no digits
+    }
+    if (errno == ERANGE) {
+        return 0; // overflow/underflow
+    }
+    if (v > ULONG_MAX) {
+        errno = ERANGE;
+        return 0; // out of int range
+    }
+    return (BBULONGINT)v;
+}
+
+BBBYTE bbStrToByte(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBULONG v = bbStrToULong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0; // no digits
+    }
+    if (errno == ERANGE) {
+        return 0; // overflow/underflow
+    }
+    if (v > UCHAR_MAX) {
+        errno = ERANGE;
+        return 0; // out of int range
+    }
+    return (BBBYTE)v;
+}
+
+BBSHORT bbStrToShort(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBULONG v = bbStrToULong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0; // no digits
+    }
+    if (errno == ERANGE) {
+        return 0; // overflow/underflow
+    }
+    if (v > USHRT_MAX) {
+        errno = ERANGE;
+        return 0; // out of int range
+    }
+    return (BBSHORT)v;
+}
+
+BBUINT bbStrToUInt(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBULONG v = bbStrToULong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0; // no digits
+    }
+    if (errno == ERANGE) {
+        return 0; // overflow/underflow
+    }
+    if (v > UINT_MAX) {
+        errno = ERANGE;
+        return 0; // out of int range
+    }
+    return (BBUINT)v;
+}
+
+BBLONG bbStrToLong(const BBChar *s, int length, int *end_index) {
+    BBLONG acc, cutoff;
+    int i, any, neg, base, overflowed, digits_start, cutlim, d;
+
+    if (end_index) {
+        *end_index = 0;
+    }
+    if (!s || length <= 0) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    i = 0;
+    while (i < length && bbIsspace(s[i])) ++i;
+    if (i >= length) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    neg = 0;
+    if (s[i] == '+' || s[i] == '-') {
+        neg = (s[i] == '-');
+        ++i;
+    }
+
+    base = 10;
+    if (i < length) {
+        if (s[i] == '%') {
+            base = 2;
+            ++i;
+        } else if (s[i] == '$') {
+            base = 16;
+            ++i;
+        }
+    }
+
+    acc = 0;
+    any = 0;
+    overflowed = 0;
+
+    /* negative-accumulation guards LLONG_MIN properly */
+    cutoff = neg ? LLONG_MIN : -LLONG_MAX;     /* negative */
+    cutlim = (int)(-(cutoff % (BBLONG)base));  /* 0..base-1 */
+    cutoff /= (BBLONG)base;
+
+    digits_start = i;
+
+    for (; i < length; ++i) {
+        d = bbGetdigit(s[i], base);
+
+        if (d < 0) {
+            break;
+        }
+
+        if (!overflowed) {
+            if (acc < cutoff || (acc == cutoff && d > cutlim)) {
+                overflowed = 1;   /* keep scanning to set end_index correctly */
+            } else {
+                acc = acc * (BBLONG)base - (BBLONG)d; /* stay negative */
+            }
+        }
+        any = 1;
+    }
+
+    if (!any || i == digits_start) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    if (end_index) {
+        *end_index = i;  /* first unparsed char (one-past-last) */
+    }
+
+    if (overflowed) {
+        errno = ERANGE;
+        return neg ? LLONG_MIN : LLONG_MAX;
+    }
+
+    errno = 0;
+    return neg ? acc : -acc;
+}
+
+BBULONG bbStrToULong(const BBChar *s, int length, int *end_index) {
+    int i, neg, base, any, overflowed, digits_start, d;
+    BBULONG acc, cutoff, cutlim;
+
+    if (end_index) {
+        *end_index = 0;
+    }
+    if (!s || length <= 0) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    i = 0;
+    while (i < length && bbIsspace(s[i])) ++i;
+    if (i >= length) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    /* optional sign */
+    neg = 0;
+    if (s[i] == '+' || s[i] == '-') {
+        neg = (s[i] == '-');
+        ++i;
+    }
+
+    /* optional BlitzMax prefix after sign: % (bin) or $ (hex); default base 10 */
+    base = 10;
+    if (i < length) {
+        if (s[i] == '%') {
+            base = 2;
+            ++i;
+        } else if (s[i] == '$') {
+            base = 16;
+            ++i;
+        }
+    }
+
+    acc = 0;
+    any = 0;
+    overflowed = 0;
+
+    /* cutoff/cutlim for unsigned accumulation: acc*base + d must not exceed ULONG_MAX */
+    cutoff = ULLONG_MAX / (BBULONG)base;
+    cutlim = ULLONG_MAX % (BBULONG)base;
+
+    digits_start = i;
+
+    for (; i < length; ++i) {
+        d = bbGetdigit(s[i], base);
+        if (d < 0) {
+            break;
+        }
+
+        if (!overflowed) {
+            if (acc > cutoff || (acc == cutoff && (BBULONG)d > cutlim)) {
+                overflowed = 1;  /* keep scanning digits to place end_index correctly */
+            } else {
+                acc = acc * (BBULONG)base + (BBULONG)d;
+            }
+        }
+        any = 1;
+    }
+
+    if (!any || i == digits_start) {
+        errno = EINVAL;
+        return 0;
+    }
+
+    if (end_index) {
+        *end_index = i;   /* first unparsed character (one-past-last) */
+    }
+
+    if (overflowed) {
+        errno = ERANGE;
+        return ULLONG_MAX;
+    }
+
+    errno = 0;
+    /* POSIX strtoul semantics: accept '-' and convert via unsigned wrap */
+    return neg ? (BBULONG)(0 - acc) : acc;
+}
+
+BBSIZET bbStrToSizet(const BBChar *s, int length, int *end_index) {
+    errno = 0;
+    int last;
+    BBULONG v = bbStrToULong(s, length, &last);
+    if (end_index) {
+        *end_index = last;
+    }
+
+    if (last <= 0) {
+        return 0;           // no digits
+    }
+    if (errno == ERANGE) {
+        return 0;           // overflow/underflow
+    }
+    if (v > SIZE_MAX) {
+        errno = ERANGE;
+        return 0;               // out of int range
+    }
+    return (BBSIZET)v;
+}
+
+BBDOUBLE bbStrToDouble(const BBChar *s, int length, int *end_index) {
+    double value = 0.0;
+    int res = bbStrToDoubleEx(s, length, &value, 0, length, 0, &bbEmptyString);
+    if (end_index) {
+        *end_index = res;
+    }
+    return value;
+}
+
+BBFLOAT bbStrToFloat(const BBChar *s, int length, int *end_index) {
+    float value = 0.0f;
+    int res = bbStrToFloatEx(s, length, &value, 0, length, 0, &bbEmptyString);
+    if (end_index) {
+        *end_index = res;
+    }
+    return value;
+}

+ 27 - 0
blitz.mod/blitz_strto.h

@@ -0,0 +1,27 @@
+
+#ifndef BLITZ_STRTO_H
+#define BLITZ_STRTO_H
+
+#include "blitz_types.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+BBINT bbStrToInt(const BBChar *s, int length, int *end_index);
+BBSHORT bbStrToShort(const BBChar *s, int length, int *end_index);
+BBBYTE bbStrToByte(const BBChar *s, int length, int *end_index);
+BBLONG bbStrToLong(const BBChar *s, int length, int *end_index);
+BBUINT bbStrToUInt(const BBChar *s, int length, int *end_index);
+BBULONG bbStrToULong(const BBChar *s, int length, int *end_index);
+BBLONGINT bbStrToLongInt(const BBChar *s, int length, int *end_index);
+BBULONGINT bbStrToULongInt(const BBChar *s, int length, int *end_index);
+BBSIZET bbStrToSizet(const BBChar *s, int length, int *end_index);
+BBFLOAT bbStrToFloat(const BBChar *s, int length, int *end_index);
+BBDOUBLE bbStrToDouble(const BBChar *s, int length, int *end_index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 584 - 0
blitz.mod/tests/test.bmx

@@ -478,3 +478,587 @@ Type TStringFromUTF8BytesTest Extends TTest
     End Method
 	
 End Type
+
+Extern
+	Function bbStrToInt:Int(s:Short Ptr, length:Int, end_index:Int Ptr)="int bbStrToInt(const BBChar *,int,int*)"
+	Function bbStrToLong:Long(s:Short Ptr, length:Int, end_index:Int Ptr)="BBLONG bbStrToLong(const BBChar *,int,int*)"
+	Function bbStrToUInt:UInt(s:Short Ptr, length:Int, end_index:Int Ptr)="BBUINT bbStrToUInt(const BBChar *,int,int*)"
+	Function bbStrToShort:Short(s:Short Ptr, length:Int, end_index:Int Ptr)="BBSHORT bbStrToShort(const BBChar *,int,int*)"
+	Function bbStrToByte:Byte(s:Short Ptr, length:Int, end_index:Int Ptr)="BBBYTE bbStrToByte(const BBChar *,int,int*)"
+	Function bbStrToULong:ULong(s:Short Ptr, length:Int, end_index:Int Ptr)="BBULONG bbStrToULong(const BBChar *,int,int*)"
+End Extern
+
+Const INT_MAX:Int = 2147483647
+Const INT_MIN:Int = -2147483648
+
+' Set this to False if your C parser stops updating end_index on overflow (i.e., leaves it on the last valid digit).
+Const OVERFLOW_CONSUMES_ALL_DIGITS:Int = True
+
+Type TIntCase
+	Field s:String
+	Field expected:Int
+	Field consumed:Int
+	Field isOverflow:Int  ' 1 if this row expects overflow clamping
+
+	Method New(s:String, expected:Int, consumed:Int, isOverflow:Int = False)
+		self.s = s
+		self.expected = expected
+		self.consumed = consumed
+		self.isOverflow = isOverflow
+	End Method
+
+	' Adjust consumed for implementations that don't advance end_index across overflow digits.
+	Method AdjustConsumed:Int()
+		If isOverflow And Not OVERFLOW_CONSUMES_ALL_DIGITS Then Return consumed - 1
+		Return consumed
+	End Method
+
+	Method ParseWithBBInt:Int(consumed:Int Var)
+		' Prepare UTF-16 buffer
+		Local n:Size_t = s.Length + 1
+		Local buf:Short[] = New Short[n]
+		s.ToWStringBuffer(buf, n)
+		' Call the native parser
+		Local end_index:Int = -1
+		Local value:Int = bbStrToInt(buf, Int(n), VarPtr end_index)
+
+		consumed = end_index
+		Return value
+	End Method
+End Type
+
+Const LONG_MAX:Long = (Long(1) Shl 63) - 1
+Const LONG_MIN:Long = - (Long(1) Shl 63)
+
+Type TLongCase
+	Field s:String
+	Field expected:Long
+	Field consumed:Int
+	Field isOverflow:Int  ' 1 if this row expects overflow clamping
+
+	Method New(s:String, expected:Long, consumed:Int, isOverflow:Int = False)
+		Self.s = s
+		Self.expected = expected
+		Self.consumed = consumed
+		Self.isOverflow = isOverflow
+	End Method
+
+	' Adjust consumed for implementations that don't advance end_index across overflow digits.
+	Method AdjustConsumed:Int()
+		If isOverflow And Not OVERFLOW_CONSUMES_ALL_DIGITS Then Return consumed - 1
+		Return consumed
+	End Method
+
+	Method ParseWithBBLong:Long(consumed:Int Var)
+		' Prepare UTF-16 buffer (+1 for NUL)
+		Local n:Size_t = s.Length + 1
+		Local buf:Short[] = New Short[n]
+		s.ToWStringBuffer(buf, n)
+
+		' Call the native parser
+		Local end_index:Int = -1
+		Local value:Long = bbStrToLong(buf, Int(n), VarPtr end_index)
+
+		consumed = end_index
+		Return value
+	End Method
+End Type
+
+Type TUIntCase
+	Field s:String
+	Field expected:UInt
+	Field consumed:Int
+	Field isOverflow:Int  ' treat as overflow/out-of-range (end_index rules may differ)
+
+	Method New(s:String, expected:UInt, consumed:Int, isOverflow:Int = False)
+		Self.s = s
+		Self.expected = expected
+		Self.consumed = consumed
+		Self.isOverflow = isOverflow
+	End Method
+
+	' Adjust consumed if your C impl stops at last valid digit instead of scanning all digits.
+	Method AdjustConsumed:Int()
+		If isOverflow And Not OVERFLOW_CONSUMES_ALL_DIGITS Then Return consumed - 1
+		Return consumed
+	End Method
+
+	Method ParseWithBBUInt:UInt(consumed:Int Var)
+		' Prepare UTF-16 buffer (+1 for NUL)
+		Local n:Size_t = s.Length + 1
+		Local buf:Short[] = New Short[n]
+		s.ToWStringBuffer(buf, n)
+
+		' Call the native parser
+		Local end_index:Int = -1
+		Local value:UInt = bbStrToUInt(buf, Int(n), VarPtr end_index)
+
+		consumed = end_index
+		Return value
+	End Method
+End Type
+
+Type TShortCase
+	Field s:String
+	Field expected:Short
+	Field consumed:Int
+	Field isOverflow:Int
+
+	Method New(s:String, expected:Short, consumed:Int, isOverflow:Int = False)
+		Self.s = s
+		Self.expected = expected
+		Self.consumed = consumed
+		Self.isOverflow = isOverflow
+	End Method
+
+	Method AdjustConsumed:Int()
+		If isOverflow And Not OVERFLOW_CONSUMES_ALL_DIGITS Then Return consumed - 1
+		Return consumed
+	End Method
+
+	Method ParseWithBBShort:Short(consumed:Int Var)
+		Local n:Size_t = s.Length + 1
+		Local buf:Short[] = New Short[n]
+		s.ToWStringBuffer(buf, n)
+		Local end_index:Int = -1
+		Local value:Short = bbStrToShort(buf, Int(n), VarPtr end_index)
+		consumed = end_index
+		Return value
+	End Method
+End Type
+
+Type TByteCase
+	Field s:String
+	Field expected:Byte
+	Field consumed:Int
+	Field isOverflow:Int
+
+	Method New(s:String, expected:Byte, consumed:Int, isOverflow:Int = False)
+		Self.s = s
+		Self.expected = expected
+		Self.consumed = consumed
+		Self.isOverflow = isOverflow
+	End Method
+
+	Method AdjustConsumed:Int()
+		If isOverflow And Not OVERFLOW_CONSUMES_ALL_DIGITS Then Return consumed - 1
+		Return consumed
+	End Method
+
+	Method ParseWithBBByte:Byte(consumed:Int Var)
+		Local n:Size_t = s.Length + 1
+		Local buf:Short[] = New Short[n]
+		s.ToWStringBuffer(buf, n)
+		Local end_index:Int = -1
+		Local value:Byte = bbStrToByte(buf, Int(n), VarPtr end_index)
+		consumed = end_index
+		Return value
+	End Method
+End Type
+
+Const UL64_MAX:ULong = ULong(-1)  ' 0xFFFFFFFFFFFFFFFF
+
+Type TULongCase
+	Field s:String
+	Field expected:ULong
+	Field consumed:Int
+	Field isOverflow:Int
+
+	Method New(s:String, expected:ULong, consumed:Int, isOverflow:Int = False)
+		Self.s = s
+		Self.expected = expected
+		Self.consumed = consumed
+		Self.isOverflow = isOverflow
+	End Method
+
+	Method AdjustConsumed:Int()
+		If isOverflow And Not OVERFLOW_CONSUMES_ALL_DIGITS Then Return consumed - 1
+		Return consumed
+	End Method
+
+	Method ParseWithBBULong:ULong(consumed:Int Var)
+		Local n:Size_t = s.Length + 1
+		Local buf:Short[] = New Short[n]
+		s.ToWStringBuffer(buf, n)
+		Local end_index:Int = -1
+		Local value:ULong = bbStrToULong(buf, Int(n), VarPtr end_index)
+		consumed = end_index
+		Return value
+	End Method
+End Type
+
+Function RepeatChar:String(ch:String, count:Int)
+	Local s:String = ""
+	For Local i:Int = 0 Until count
+		s :+ ch
+	Next
+	Return s
+End Function
+
+Type TStringToNumStrToNumTest Extends TTest
+
+		' Safer equality for ULong without relying on string/overloads.
+	Function AssertULongEquals(expected:ULong, actual:ULong, msg:String)
+		assertEquals(Int(expected Shr 32), Int(actual Shr 32), msg + " (hi)")
+		assertEquals(Int(expected & $FFFFFFFF), Int(actual & $FFFFFFFF), msg + " (lo)")
+	End Function
+
+	Method test_ToInt_All() { test }
+		Local cases:TIntCase[] = [ ..
+			..' --- Decimal (base 10)
+			New TIntCase("0",                      0,                  1),
+			New TIntCase("123",                    123,                3),
+			New TIntCase("-123",                   -123,               4),
+			New TIntCase("   +42",                 42,                 6),
+			New TIntCase("   -0",                  0,                  5),
+			New TIntCase("00123",                  123,                5),
+			New TIntCase("123abc",                 123,                3),
+			New TIntCase("123   ",                 123,                3),
+
+			' Exact limits
+			New TIntCase("2147483647",             INT_MAX,            10),
+			New TIntCase("-2147483648",            INT_MIN,            11),
+
+			' Overflows (clamp; mark with isOverflow=1)
+			New TIntCase("2147483648",             INT_MAX,            10, True),
+			New TIntCase("-2147483649",            INT_MIN,            11, True),
+
+			' Errors (no digits)
+			New TIntCase("",                       0,                  0),
+			New TIntCase("   ",                    0,                  0),
+			New TIntCase("+",                      0,                  0),
+			New TIntCase("-",                      0,                  0),
+			New TIntCase("abc",                    0,                  0),
+
+			' --- Hex with $ (base 16)
+			New TIntCase("$7FFFFFFF",              INT_MAX,            9),
+			New TIntCase("-$80000000",             INT_MIN,            10),
+			New TIntCase("$80000000",              INT_MAX,            9,  True),     ' +2^31 overflows
+			New TIntCase("-$80000001",             INT_MIN,            10, True),     ' -(2^31+1) overflows
+			New TIntCase("$7fffffff",              INT_MAX,            9),            ' lowercase
+			New TIntCase("   $1a",                 26,                 6),
+			New TIntCase("$",                      0,                  0),            ' no digits after prefix
+			New TIntCase("$G1",                    0,                  0),            ' first is invalid -> no digits
+			New TIntCase("$FFxyz",                 255,                3),            ' stops at first invalid
+
+			' --- Binary with % (base 2)
+			New TIntCase("%0",                     0,                  2),
+			New TIntCase("%101010",                42,                 7),
+			New TIntCase("-%10000000000000000000000000000000", INT_MIN, 34),          ' -2^31
+			New TIntCase("%10000000000000000000000000000000",  INT_MAX, 33, True),    ' +2^31 overflows
+			New TIntCase("%1002",                  4,                  4),            ' stops at '2'
+			New TIntCase("%",                      0,                  0),            ' no digits after prefix
+			New TIntCase("   %101",                5,                  7),
+
+			' --- With +sign and prefixes
+			New TIntCase("+$7fffffff",             INT_MAX,            10),
+			New TIntCase("+%101",                  5,                  5),
+
+			' --- Trailing junk after signed number
+			New TIntCase("-42xyz",                 -42,                3)..
+		]
+
+		For Local t:TIntCase = EachIn cases
+			Local gotConsumed:Int
+			Local gotValue:Int = t.ParseWithBBInt(gotConsumed)
+			Local expectConsumed:Int = t.AdjustConsumed()
+
+			assertEquals(expectConsumed, gotConsumed, "consumed mismatch for '"+t.s+"'")
+			assertEquals(t.expected, gotValue, "value mismatch for '"+t.s+"'")
+		Next
+	End Method
+
+	Method test_ToLong_All() { test }
+		' Build the 64-bit binary limit strings:
+		' -%1 + 63 zeros  (=-2^63) and  %1 + 63 zeros (=+2^63, overflow)
+		Local z63:String = "0000000000000000000000000000000" + "00000000000000000000000000000000" ' 31 + 32 = 63 zeros
+		Local BIN64_MIN:String = "-%1" + z63
+		Local BIN64_POS_OVF:String = "%1" + z63
+
+		Local cases:TLongCase[] = [..
+			..' --- Decimal (base 10)
+			New TLongCase("0",                        0,                    1),
+			New TLongCase("123",                      123,                  3),
+			New TLongCase("-123",                     -123,                 4),
+			New TLongCase("   +42",                   42,                   6),
+			New TLongCase("   -0",                    0,                    5),
+			New TLongCase("00123",                    123,                  5),
+			New TLongCase("123abc",                   123,                  3),
+			New TLongCase("123   ",                   123,                  3),
+
+			' Exact 64-bit limits
+			New TLongCase("9223372036854775807",      LONG_MAX,             19),
+			New TLongCase("-9223372036854775808",     LONG_MIN,             20),
+
+			' Overflows (clamp; mark with isOverflow=1)
+			New TLongCase("9223372036854775808",      LONG_MAX,             19, True),
+			New TLongCase("-9223372036854775809",     LONG_MIN,             20, True),
+
+			' Errors (no digits)
+			New TLongCase("",                         0,                    0),
+			New TLongCase("   ",                      0,                    0),
+			New TLongCase("+",                        0,                    0),
+			New TLongCase("-",                        0,                    0),
+			New TLongCase("abc",                      0,                    0),
+
+			' --- Hex with $ (base 16)
+			New TLongCase("$7FFFFFFFFFFFFFFF",        LONG_MAX,             17),
+			New TLongCase("-$8000000000000000",       LONG_MIN,             18),
+			New TLongCase("$8000000000000000",        LONG_MAX,             17, True), ' +2^63 overflows
+			New TLongCase("-$8000000000000001",       LONG_MIN,             18, True), ' -(2^63+1) overflows
+			New TLongCase("$7fffffffffffffff",        LONG_MAX,             17),       ' lowercase
+			New TLongCase("   $1a",                   26,                   6),
+			New TLongCase("$",                        0,                    0),        ' no digits after prefix
+			New TLongCase("$G1",                      0,                    0),        ' first is invalid -> no digits
+			New TLongCase("$FFxyz",                   255,                  3),        ' stops at first invalid
+
+			' --- Binary with % (base 2)
+			New TLongCase("%0",                       0,                    2),
+			New TLongCase("%101010",                  42,                   7),
+			New TLongCase(BIN64_MIN,                  LONG_MIN,             66),       ' -2^63
+			New TLongCase(BIN64_POS_OVF,              LONG_MAX,             65, True), ' +2^63 overflows
+			New TLongCase("%1002",                    4,                    4),        ' stops at '2'
+			New TLongCase("%",                        0,                    0),        ' no digits after prefix
+			New TLongCase("   %101",                  5,                    7),
+
+			' --- With +sign and prefixes
+			New TLongCase("+$7fffffffffffffff",       LONG_MAX,             18),
+			New TLongCase("+%101",                    5,                    5),
+
+			' --- Trailing junk after signed number
+			New TLongCase("-42xyz",                   -42,                  3)..
+		]
+
+		For Local t:TLongCase = EachIn cases
+			Local gotConsumed:Int
+			Local gotValue:Long = t.ParseWithBBLong(gotConsumed)
+			Local expectConsumed:Int = t.AdjustConsumed()
+
+			assertEquals(expectConsumed, gotConsumed, "consumed mismatch for '"+t.s+"'")
+			assertEquals(t.expected, gotValue, "value mismatch for '"+t.s+"'")
+		Next
+	End Method
+
+	Method test_ToUInt_All() { test }
+		' Prebuilt strings for 32-bit edges
+		Local BIN32_MAX:String = "%11111111111111111111111111111111" ' 32 ones
+		Local zeros32:String = "00000000000000000000000000000000"  ' 32 zeros
+		Local BIN32_POS_OVF:String = "%1" + zeros32                   ' 2^32 -> overflow
+
+		Local cases:TUIntCase[] = [..
+			..' --- Decimal (base 10)
+			New TUIntCase("0",                       UInt(0),                 1),
+			New TUIntCase("123",                     UInt(123),               3),
+			New TUIntCase("+123",                    UInt(123),               4),
+			New TUIntCase("   +42",                  UInt(42),                6),
+			New TUIntCase("   -0",                   UInt(0),                 5),     ' -0 is still 0
+			New TUIntCase("00123",                   UInt(123),               5),
+			New TUIntCase("123abc",                  UInt(123),               3),
+			New TUIntCase("123   ",                  UInt(123),               3),
+
+			' Range edges
+			New TUIntCase("2147483647",              UInt($7FFFFFFF),         10),    ' INT_MAX fits in UInt
+			New TUIntCase("2147483648",              UInt($80000000),         10),    ' 2^31 fits
+			New TUIntCase("4294967295",              UInt($FFFFFFFF),         10),    ' UINT_MAX
+			New TUIntCase("4294967296",              UInt(0),                 10, True), ' overflow -> 0
+
+			' Negatives are out-of-range (wrapper returns 0) but digits are consumed
+			New TUIntCase("-1",                      UInt(0),                 2,  True),
+			New TUIntCase("-",                       UInt(0),                 0),
+			New TUIntCase("+",                       UInt(0),                 0),
+
+			' Errors (no digits)
+			New TUIntCase("",                        UInt(0),                 0),
+			New TUIntCase("   ",                     UInt(0),                 0),
+			New TUIntCase("abc",                     UInt(0),                 0),
+
+			' --- Hex with $ (base 16)
+			New TUIntCase("$0",                      UInt(0),                 2),
+			New TUIntCase("$FFFFFFFF",               UInt($FFFFFFFF),         9),
+			New TUIntCase("$7FFFFFFF",               UInt($7FFFFFFF),         9),
+			New TUIntCase("$80000000",               UInt($80000000),         9),
+			New TUIntCase("$100000000",              UInt(0),                 10, True),   ' 0x1_0000_0000 overflow
+			New TUIntCase("-$1",                     UInt(0),                 3,  True),   ' negative -> out-of-range
+			New TUIntCase("$7fffffff",               UInt($7FFFFFFF),         9),         ' lowercase
+			New TUIntCase("   $1a",                  UInt(26),                6),
+			New TUIntCase("$",                       UInt(0),                 0),         ' no digits after prefix
+			New TUIntCase("$G1",                     UInt(0),                 0),         ' first is invalid -> no digits
+			New TUIntCase("$FFxyz",                  UInt(255),               3),         ' stops at first invalid
+			New TUIntCase("+$FFFFFFFF",              UInt($FFFFFFFF),         10),
+
+			' --- Binary with % (base 2)
+			New TUIntCase("%0",                      UInt(0),                 2),
+			New TUIntCase("%101010",                 UInt(42),                7),
+			New TUIntCase(BIN32_MAX,                 UInt($FFFFFFFF),         33),       ' 32 ones
+			New TUIntCase("%10000000000000000000000000000000", UInt($80000000), 33),     ' 2^31
+			New TUIntCase(BIN32_POS_OVF,             UInt(0),                 34, True), ' 2^32 -> overflow
+			New TUIntCase("%1002",                   UInt(4),                 4),        ' stops at '2'
+			New TUIntCase("%",                       UInt(0),                 0),        ' no digits after prefix
+			New TUIntCase("   %101",                 UInt(5),                 7),
+			New TUIntCase("+%101",                   UInt(5),                 5),
+			New TUIntCase("-%1",                     UInt(0),                 3,  True)..  ' negative -> out-of-range
+		]
+
+		For Local t:TUIntCase = EachIn cases
+			Local gotConsumed:Int
+			Local gotValue:UInt = t.ParseWithBBUInt(gotConsumed)
+			Local expectConsumed:Int = t.AdjustConsumed()
+
+			assertEquals(expectConsumed, gotConsumed, "consumed mismatch for '"+t.s+"'")
+			' Cast to Long in assertion to avoid any UInt overload ambiguity
+			assertEquals(Long(t.expected), Long(gotValue), "value mismatch for '"+t.s+"'")
+		Next
+	End Method
+
+	Method test_ToShort_All() { test }
+		Local BIN16_MAX:String = "%" + RepeatChar("1", 16)
+		Local BIN16_POS_OVF:String = "%1" + RepeatChar("0", 16)  ' 2^16
+
+		Local cases:TShortCase[] = [..
+			..' Decimal
+			New TShortCase("0",           Short(0),       1),
+			New TShortCase("65535",       Short(65535),   5),
+			New TShortCase("65536",       Short(0),       5, True),
+			New TShortCase("123abc",      Short(123),     3),
+			New TShortCase("   +42",      Short(42),      6),
+			New TShortCase("-1",          Short(0),       2, True),
+
+			' Errors
+			New TShortCase("",            Short(0),       0),
+			New TShortCase("   ",         Short(0),       0),
+			New TShortCase("+",           Short(0),       0),
+			New TShortCase("-",           Short(0),       0),
+			New TShortCase("abc",         Short(0),       0),
+
+			' Hex ($)
+			New TShortCase("$0",          Short(0),       2),
+			New TShortCase("$FFFF",       Short(65535),   5),
+			New TShortCase("$10000",      Short(0),       6, True),
+			New TShortCase("$fffe",       Short(65534),   5),
+			New TShortCase("+$00FF",      Short(255),     6),
+			New TShortCase("-$1",         Short(0),       3, True),
+			New TShortCase("$",           Short(0),       0),
+			New TShortCase("$G1",         Short(0),       0),
+			New TShortCase("$FFxyz",      Short(255),     3),
+
+			' Binary (%)
+			New TShortCase("%0",          Short(0),       2),
+			New TShortCase("%1111111111111111", Short(65535), 17),
+			New TShortCase(BIN16_POS_OVF, Short(0),       18, True),
+			New TShortCase("%1002",       Short(4),       4),
+			New TShortCase("%",           Short(0),       0),
+			New TShortCase("   %101",     Short(5),       7)..
+		]
+
+		For Local t:TShortCase = EachIn cases
+			Local gotConsumed:Int
+			Local gotValue:Short = t.ParseWithBBShort(gotConsumed)
+			assertEquals(t.AdjustConsumed(), gotConsumed, "consumed mismatch for '"+t.s+"'")
+			assertEquals(Int(t.expected), Int(gotValue), "value mismatch for '"+t.s+"'")
+		Next
+	End Method
+
+	Method test_ToByte_All() { test }
+		Local BIN8_MAX:String = "%" + RepeatChar("1", 8)
+		Local BIN8_POS_OVF:String = "%1" + RepeatChar("0", 8)   ' 2^8
+
+		Local cases:TByteCase[] = [..
+			..' Decimal
+			New TByteCase("0",        Byte(0),     1),
+			New TByteCase("255",      Byte(255),   3),
+			New TByteCase("256",      Byte(0),     3, True),
+			New TByteCase("123abc",   Byte(123),   3),
+			New TByteCase("   +42",   Byte(42),    6),
+			New TByteCase("-1",       Byte(0),     2, True),
+
+			' Errors
+			New TByteCase("",         Byte(0),     0),
+			New TByteCase("   ",      Byte(0),     0),
+			New TByteCase("+",        Byte(0),     0),
+			New TByteCase("-",        Byte(0),     0),
+			New TByteCase("abc",      Byte(0),     0),
+
+			' Hex ($)
+			New TByteCase("$0",       Byte(0),     2),
+			New TByteCase("$FF",      Byte(255),   3),
+			New TByteCase("$100",     Byte(0),     4, True),
+			New TByteCase("$7f",      Byte(127),   3),
+			New TByteCase("+$0A",     Byte(10),    4),
+			New TByteCase("-$1",      Byte(0),     3, True),
+			New TByteCase("$",        Byte(0),     0),
+			New TByteCase("$G1",      Byte(0),     0),
+			New TByteCase("$FFxyz",   Byte(255),   3),
+
+			' Binary (%)
+			New TByteCase("%0",       Byte(0),     2),
+			New TByteCase(BIN8_MAX,   Byte(255),   9),
+			New TByteCase(BIN8_POS_OVF, Byte(0),   10, True),
+			New TByteCase("%1002",    Byte(4),     4),
+			New TByteCase("%",        Byte(0),     0),
+			New TByteCase("   %1010", Byte(10),    8),
+			New TByteCase("+%1010",   Byte(10),    6)..
+		]
+
+		For Local t:TByteCase = EachIn cases
+			Local gotConsumed:Int
+			Local gotValue:Byte = t.ParseWithBBByte(gotConsumed)
+			assertEquals(t.AdjustConsumed(), gotConsumed, "consumed mismatch for '"+t.s+"'")
+			assertEquals(Int(t.expected), Int(gotValue), "value mismatch for '"+t.s+"'")
+		Next
+	End Method
+
+		Method test_ToULong_All() { test }
+		Local decMax:String = "18446744073709551615"      ' 2^64 - 1
+		Local decOvf:String = "18446744073709551616"      ' 2^64
+
+		Local BIN64_MAX:String = "%" + RepeatChar("1", 64)
+		Local BIN64_POS_OVF:String = "%1" + RepeatChar("0", 64)
+
+		Local cases:TULongCase[] = [..
+			..' Decimal
+			New TULongCase("0",                 ULong(0),         1),
+			New TULongCase("123",               ULong(123),       3),
+			New TULongCase("   +42",            ULong(42),        6),
+			New TULongCase(decMax,              UL64_MAX,        20),
+			New TULongCase(decOvf,              UL64_MAX,        20, True),  ' clamp on overflow
+
+			' Accept leading '-' with wrap (POSIX strtoul style)
+			New TULongCase("-1",                UL64_MAX,         2),
+			New TULongCase("-0",                ULong(0),         2),
+
+			' Errors
+			New TULongCase("",                  ULong(0),         0),
+			New TULongCase("   ",               ULong(0),         0),
+			New TULongCase("+",                 ULong(0),         0),
+			New TULongCase("-",                 ULong(0),         0),
+			New TULongCase("abc",               ULong(0),         0),
+
+			' Hex ($)
+			New TULongCase("$0",                ULong(0),         2),
+			New TULongCase("$FFFFFFFFFFFFFFFF", UL64_MAX,        17),
+			New TULongCase("$10000000000000000", UL64_MAX,       18, True),  ' overflow
+			New TULongCase("-$1",               UL64_MAX,         3),        ' wrap
+			New TULongCase("$FFxyz",            ULong(255),       3),
+			New TULongCase("$",                 ULong(0),         0),
+			New TULongCase("$G1",               ULong(0),         0),
+			New TULongCase("+$FFFFFFFFFFFFFFFF", UL64_MAX,       18),
+
+			' Binary (%)
+			New TULongCase("%0",                ULong(0),         2),
+			New TULongCase(BIN64_MAX,           UL64_MAX,        65),
+			New TULongCase(BIN64_POS_OVF,       UL64_MAX,        66, True),
+			New TULongCase("-%1",               UL64_MAX,         3),        ' wrap
+			New TULongCase("%1002",             ULong(4),         4),
+			New TULongCase("%",                 ULong(0),         0),
+			New TULongCase("   %101",           ULong(5),         7)..
+		]
+
+		For Local t:TULongCase = EachIn cases
+			Local gotConsumed:Int
+			Local gotValue:ULong = t.ParseWithBBULong(gotConsumed)
+			assertEquals(t.AdjustConsumed(), gotConsumed, "consumed mismatch for '"+t.s+"'")
+			AssertULongEquals(t.expected, gotValue, "value mismatch for '"+t.s+"'")
+		Next
+	End Method
+
+End Type

+ 12 - 1
stringbuilder.mod/common.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2018-2024 Bruce A Henderson
+' Copyright (c) 2018-2025 Bruce A Henderson
 ' 
 ' This software is provided 'as-is', without any express or implied
 ' warranty. In no event will the authors be held liable for any damages
@@ -84,6 +84,17 @@ Extern
 	Function bmx_stringbuilder_hash:ULong(buffer:Byte Ptr)
 	Function bmx_stringbuilder_append_utf32string(buffer:Byte Ptr, chars:UInt Ptr)
 	Function bmx_stringbuilder_append_utf32bytes(buffer:Byte Ptr, chars:UInt Ptr, length:Int)
+	Function bmx_stringbuilder_toint:Int(buffer:Byte Ptr)
+	Function bmx_stringbuilder_tolong:Long(buffer:Byte Ptr)
+	Function bmx_stringbuilder_touint:UInt(buffer:Byte Ptr)
+	Function bmx_stringbuilder_toshort:Short(buffer:Byte Ptr)
+	Function bmx_stringbuilder_tobyte:Byte(buffer:Byte Ptr)
+	Function bmx_stringbuilder_toulong:ULong(buffer:Byte Ptr)
+	Function bmx_stringbuilder_tolongint:LongInt(buffer:Byte Ptr)
+	Function bmx_stringbuilder_toulongint:ULongInt(buffer:Byte Ptr)
+	Function bmx_stringbuilder_tosizet:Size_T(buffer:Byte Ptr)
+	Function bmx_stringbuilder_tofloat:Float(buffer:Byte Ptr)
+	Function bmx_stringbuilder_todouble:Double(buffer:Byte Ptr)
 
 	Function bmx_stringbuilder_splitbuffer_length:Int(splitPtr:Byte Ptr)
 	Function bmx_stringbuilder_splitbuffer_text:String(splitPtr:Byte Ptr, index:Int)

+ 98 - 114
stringbuilder.mod/glue.c

@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2018-2024 Bruce A Henderson
+  Copyright (c) 2018-2025 Bruce A Henderson
   
   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any damages
@@ -990,6 +990,94 @@ void bmx_stringbuilder_append_utf32bytes(struct MaxStringBuilder * buf, BBUINT *
 	buf->hash = 0;
 }
 
+int bmx_stringbuilder_toint(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToInt(buf->buffer, buf->count, NULL);
+}
+
+BBLONG bmx_stringbuilder_tolong(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToLong(buf->buffer, buf->count, NULL);
+}
+
+BBUINT bmx_stringbuilder_touint(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToUInt(buf->buffer, buf->count, NULL);
+}
+
+BBSHORT bmx_stringbuilder_toshort(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToShort(buf->buffer, buf->count, NULL);
+}
+
+BBBYTE bmx_stringbuilder_tobyte(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToByte(buf->buffer, buf->count, NULL);
+}
+
+BBULONG bmx_stringbuilder_toulong(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToULong(buf->buffer, buf->count, NULL);
+}
+
+BBLONGINT bmx_stringbuilder_tolongint(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToLongInt(buf->buffer, buf->count, NULL);
+}
+
+BBULONGINT bmx_stringbuilder_toulongint(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToULongInt(buf->buffer, buf->count, NULL);
+}
+
+BBSIZET bmx_stringbuilder_tosizet(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToSizet(buf->buffer, buf->count, NULL);
+}
+
+BBFLOAT bmx_stringbuilder_tofloat(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToFloat(buf->buffer, buf->count, NULL);
+}
+
+BBDOUBLE bmx_stringbuilder_todouble(struct MaxStringBuilder * buf) {
+	if (buf->count == 0) {
+		return 0;
+	}
+
+	return bbStrToDouble(buf->buffer, buf->count, NULL);
+}
+
 /* ----------------------------------------------------- */
 
 int bmx_stringbuilder_splitbuffer_length(struct MaxSplitBuffer * buf) {
@@ -1101,23 +1189,7 @@ int bmx_stringbuilder_splitbuffer_toint(struct MaxSplitBuffer * splitBuffer, int
 
     BBChar *segment = (BBChar *)&(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    long result = strtol(numbuf, &endPtr, 10);
-
-    // Make sure that endPtr does not exceed the bounds of the segment
-    if (endPtr > numbuf + length) {
-        return 0;
-    }
-
-    if (errno == ERANGE || result > INT_MAX || result < INT_MIN || endPtr == numbuf) {
-        return 0;
-    }
-
-    return (int)result;
+	return bbStrToInt(segment, length, NULL);
 }
 
 unsigned int bmx_stringbuilder_splitbuffer_touint(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1135,18 +1207,7 @@ unsigned int bmx_stringbuilder_splitbuffer_touint(struct MaxSplitBuffer * splitB
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    unsigned long result = strtoul(numbuf, &endPtr, 10);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf || result > UINT_MAX) {
-        return 0;
-    }
-
-    return (unsigned int)result;
+	return bbStrToUInt(segment, length, NULL);
 }
 
 float bmx_stringbuilder_splitbuffer_tofloat(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1164,18 +1225,7 @@ float bmx_stringbuilder_splitbuffer_tofloat(struct MaxSplitBuffer * splitBuffer,
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    float result = strtof(numbuf, &endPtr);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf) {
-        return 0.0f;
-    }
-
-    return result;
+	return bbStrToFloat(segment, length, NULL);
 }
 
 double bmx_stringbuilder_splitbuffer_todouble(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1192,19 +1242,8 @@ double bmx_stringbuilder_splitbuffer_todouble(struct MaxSplitBuffer * splitBuffe
     }
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
-	
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    double result = strtod(numbuf, &endPtr);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf) {
-        return 0.0;
-    }
 
-    return result;
+	return bbStrToDouble(segment, length, NULL);
 }
 
 BBInt64 bmx_stringbuilder_splitbuffer_tolong(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1222,18 +1261,7 @@ BBInt64 bmx_stringbuilder_splitbuffer_tolong(struct MaxSplitBuffer * splitBuffer
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    BBInt64 result = strtoll(numbuf, &endPtr, 10);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf) {
-        return 0;
-    }
-
-    return result;
+	return bbStrToLong(segment, length, NULL);
 }
 
 BBUInt64 bmx_stringbuilder_splitbuffer_toulong(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1251,18 +1279,7 @@ BBUInt64 bmx_stringbuilder_splitbuffer_toulong(struct MaxSplitBuffer * splitBuff
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    BBUInt64 result = strtoull(numbuf, &endPtr, 10);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf) {
-        return 0;
-    }
-
-    return result;
+	return bbStrToULong(segment, length, NULL);
 }
 
 size_t bmx_stringbuilder_splitbuffer_tosizet(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1280,18 +1297,7 @@ size_t bmx_stringbuilder_splitbuffer_tosizet(struct MaxSplitBuffer * splitBuffer
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    unsigned long long result = strtoull(numbuf, &endPtr, 10);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf) {
-        return 0;
-    }
-
-    return (size_t)result;
+	return bbStrToSizet(segment, length, NULL);
 }
 
 BBSHORT bmx_stringbuilder_splitbuffer_toshort(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1309,18 +1315,7 @@ BBSHORT bmx_stringbuilder_splitbuffer_toshort(struct MaxSplitBuffer * splitBuffe
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    long result = strtol(numbuf, &endPtr, 10);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf || result < 0 || result > USHRT_MAX) {
-        return 0;
-    }
-
-    return (BBSHORT)result;
+	return bbStrToShort(segment, length, NULL);
 }
 
 BBBYTE bmx_stringbuilder_splitbuffer_tobyte(struct MaxSplitBuffer * splitBuffer, int index) {
@@ -1338,16 +1333,5 @@ BBBYTE bmx_stringbuilder_splitbuffer_tobyte(struct MaxSplitBuffer * splitBuffer,
 
     BBChar *segment = (BBChar *) &(splitBuffer->buffer->buffer[start]);
 
-	char numbuf[256];
-	bmx_stringbuilder_toutf8_sbuffer(segment, length, numbuf, sizeof(numbuf));
-
-    char *endPtr;
-    errno = 0;
-    long result = strtol(numbuf, &endPtr, 10);
-
-    if (endPtr > numbuf + length || errno == ERANGE || endPtr == numbuf || result < 0 || result > UCHAR_MAX) {
-        return 0;
-    }
-
-    return (BBBYTE)result;
+	return bbStrToByte(segment, length, NULL);
 }

+ 12 - 1
stringbuilder.mod/glue.h

@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2018-2024 Bruce A Henderson
+  Copyright (c) 2018-2025 Bruce A Henderson
   
   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any damages
@@ -103,6 +103,17 @@ void bmx_stringbuilder_format_double(struct MaxStringBuilder * buf, BBString * f
 BBULONG bmx_stringbuilder_hash(struct MaxStringBuilder * buf);
 void bmx_stringbuilder_append_utf32string(struct MaxStringBuilder * buf, BBUINT * chars);
 void bmx_stringbuilder_append_utf32bytes(struct MaxStringBuilder * buf, BBUINT * chars, int length);
+int bmx_stringbuilder_toint(struct MaxStringBuilder * buf);
+BBLONG bmx_stringbuilder_tolong(struct MaxStringBuilder * buf);
+BBUINT bmx_stringbuilder_touint(struct MaxStringBuilder * buf);
+BBSHORT bmx_stringbuilder_toshort(struct MaxStringBuilder * buf);
+BBBYTE bmx_stringbuilder_tobyte(struct MaxStringBuilder * buf);
+BBULONG bmx_stringbuilder_toulong(struct MaxStringBuilder * buf);
+BBLONGINT bmx_stringbuilder_tolongint(struct MaxStringBuilder * buf);
+BBULONGINT bmx_stringbuilder_toulongint(struct MaxStringBuilder * buf);
+BBSIZET bmx_stringbuilder_tosizet(struct MaxStringBuilder * buf);
+BBFLOAT bmx_stringbuilder_tofloat(struct MaxStringBuilder * buf);
+BBDOUBLE bmx_stringbuilder_todouble(struct MaxStringBuilder * buf);
 
 /* ----------------------------------------------------- */
 

+ 102 - 3
stringbuilder.mod/stringbuilder.bmx

@@ -1,4 +1,4 @@
-' Copyright (c) 2018-2024 Bruce A Henderson
+' Copyright (c) 2018-2025 Bruce A Henderson
 ' 
 ' This software is provided 'as-is', without any express or implied
 ' warranty. In no event will the authors be held liable for any damages
@@ -23,10 +23,12 @@ bbdoc: A string builder.
 End Rem	
 Module BRL.StringBuilder
 
-ModuleInfo "Version: 1.19"
+ModuleInfo "Version: 1.20"
 ModuleInfo "License: zlib/libpng"
-ModuleInfo "Copyright: 2018-2024 Bruce A Henderson"
+ModuleInfo "Copyright: 2018-2025 Bruce A Henderson"
 
+ModuleInfo "History: 1.20"
+ModuleInfo "History: Added ToNumber methods."
 ModuleInfo "History: 1.19"
 ModuleInfo "History: Added TSplitBuffer Split() method."
 ModuleInfo "History: Added TSplitBuffer to number methods."
@@ -935,6 +937,103 @@ Public
 		Return Not bmx_stringbuilder_equals(buffer, sb.buffer)
 	End Method
 
+	Rem
+	bbdoc: Returns the string builder as an #Int.
+	about: If the string builder is not a valid #Int, 0 is returned.
+	If the calculated value is out of range for an #Int, either int max or int min is returned.
+	End Rem
+	Method ToInt:Int()
+		Return bmx_stringbuilder_toint(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #Long.
+	about: If the string builder is not a valid #Long, 0 is returned.
+	If the calculated value is out of range for a #Long, either long max or long min is returned.
+	End Rem
+	Method ToLong:Long()
+		Return bmx_stringbuilder_tolong(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #Short.
+	about: If the string builder is not a valid #Short, 0 is returned.
+	If the calculated value is out of range for a #Short, short max is returned.
+	End Rem
+	Method ToShort:Short()
+		Return bmx_stringbuilder_toshort(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #Byte.
+	about: If the string builder is not a valid #Byte, 0 is returned.
+	If the calculated value is out of range for a #Byte, byte max is returned.
+	End Rem
+	Method ToByte:Byte()
+		Return bmx_stringbuilder_tobyte(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #UInt.
+	about: If the string builder is not a valid #UInt, 0 is returned.
+	If the calculated value is out of range for a #UInt, uint max is returned.
+	End Rem
+	Method ToUInt:UInt()
+		Return bmx_stringbuilder_touint(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #ULong.
+	about: If the string builder is not a valid #ULong, 0 is returned.
+	If the calculated value is out of range for a #ULong, ulong max is returned.
+	End Rem
+	Method ToULong:ULong()
+		Return bmx_stringbuilder_toulong(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #Size_T.
+	about: If the string builder is not a valid #Size_T, 0 is returned.
+	If the calculated value is out of range for a #Size_T, Size_T max is returned.
+	End Rem
+	Method ToSizeT:Size_T()
+		Return bmx_stringbuilder_tosizet(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #LongInt.
+	about: If the string builder is not a valid #LongInt, 0 is returned.
+	If the calculated value is out of range for a #LongInt, either long int max or long int min is returned.
+	End Rem
+	Method ToLongInt:LongInt()
+		Return bmx_stringbuilder_tolongint(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #ULongInt.
+	about: If the string builder is not a valid #ULongInt, 0 is returned.
+	If the calculated value is out of range for a #ULongInt, either ulong int max or ulong int min is returned.
+	End Rem
+	Method ToULongInt:ULongInt()
+		Return bmx_stringbuilder_toulongint(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #Float.
+	about: If the string builder is not a valid #Float, 0.0 is returned.
+	End Rem
+	Method ToFloat:Float()
+		Return bmx_stringbuilder_tofloat(buffer)
+	End Method
+
+	Rem
+	bbdoc: Returns the string builder as a #Double.
+	about: If the string builder is not a valid #Double, 0.0 is returned.
+	End Rem
+	Method ToDouble:Double()
+		Return bmx_stringbuilder_todouble(buffer)
+	End Method
+
 	Method Delete()
 		If buffer Then
 			bmx_stringbuilder_free(buffer)