Error processing request

Parameters

CONTENT_LENGTH0
REQUEST_METHODGET
REQUEST_URI/revision/Tcl%5FNewByteArrayObj?V=10
QUERY_STRINGV=10
CONTENT_TYPE
DOCUMENT_URI/revision/Tcl_NewByteArrayObj
DOCUMENT_ROOT/var/www/nikit/nikit/nginx/../docroot
SCGI1
SERVER_PROTOCOLHTTP/1.1
HTTPSon
REMOTE_ADDR172.70.179.39
REMOTE_PORT18344
SERVER_PORT4443
SERVER_NAMEwiki.tcl-lang.org
HTTP_HOSTwiki.tcl-lang.org
HTTP_CONNECTIONKeep-Alive
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_X_FORWARDED_FOR18.117.182.179
HTTP_CF_RAY876eec2efdfb02c8-ORD
HTTP_X_FORWARDED_PROTOhttps
HTTP_CF_VISITOR{"scheme":"https"}
HTTP_ACCEPT*/*
HTTP_USER_AGENTMozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; [email protected])
HTTP_CF_CONNECTING_IP18.117.182.179
HTTP_CDN_LOOPcloudflare
HTTP_CF_IPCOUNTRYUS

Body


Error

Unknow state transition: LINE -> END

-code

1

-level

0

-errorstack

INNER {returnImm {Unknow state transition: LINE -> END} {}} CALL {my render_wikit Tcl_NewByteArrayObj {Tcl_Obj * '''Tcl_NewByteArrayObj'''(CONST unsigned char *''bytes'', int ''length'')

Tcl_Obj *
Tcl_NewByteArrayObj(bytes, length)

Tcl_NewByteArrayObj will create a new object of byte-array type.
Both of these procedures set the object's type to be byte-array and set the object's internal representation to a copy of the array of bytes given by bytes.
Tcl_NewByteArrayObj returns a pointer to a newly allocated object with a reference count of zero.

http://www.tcl.tk/man/tcl8.5/TclLib/ByteArrObj.htm
----
[HaO]: To populate a byte array by a C extension and return it as the result object, the following code might be used:

======
Tcl_Obj *pObj = Tcl_NewObj();
unsigned char *pChar = Tcl_SetByteArrayLength(pObj, 3);

// Dummy population functionality
pChar[0] = '\1'; pChar[1] = '\xff'; pChar[2] = '\x80';
Tcl_InvalidateStringRep(pObj);
Tcl_SetObjResult(interp,pObj); 
======
Remarks:
   * [Alexandre Ferrieux]: '''Tcl_NewObj'''() may be replaced by '''Tcl_NewByteArrayObj'''( "", 1 ).
   * [DGP]: If the object is not newly created (for example a parameter object is modified, one should check the object to be shared:
======
if ( Tcl_IsShared(pObj) )
    pObj = Tcl_DuplicateObj(pObj);
======

----
[DKF]: I don't recommend using a length of 0 to Tcl_NewByteArrayObj; the behaviour of that depends on whether malloc(0) keels over (which is system dependent).

[Duoas] That seems me fairly obnoxious. Doesn't the Tcl_NewByteArrayObj() code know that it oughtn't bother to try allocating zero bytes, and just initialize an empty/nil internal representation to begin with? [[edit2]] For that matter, would it be reasonable to specify NULL for source and a non-zero length for an uninitialized block of bytes?

[DKF]: I merely report the current state of affairs.

[Duoas] :-D  Perhaps I'll get the latest CVS and fix that sometime in the next couple of days.

[Duoas] 2008-10-31 Well, I've looked at the source (`~/tcl/generic/tclBinary.c`) and this is what I've found.

The documentation plainly states that 'length' must be greater than or equal to zero. Assuming no one ever passes a negative number, none of the functions will break. Zero is fine --malloc() will ''not'' get a zero size_t for a zero-length byte array, so it is malloc()-safe (remembering again not to give it negative values).

Frankly, I think I would insert a line above 327 that forces 'length' to be >= 0.

As for a NULL 'bytes' argument, if 'length' is zero, there is no problem. However, if 'length' is non-zero, then you will almost surely cause a segfault/access violation. Again, I would insert a little line above 330:331 just for that:
======
void
Tcl_SetByteArrayObj(
    Tcl_Obj *objPtr,		/* Object to initialize as a ByteArray. */
    const unsigned char *bytes,	/* The array of bytes to use as the new
				 * value. */
    int length)			/* Length of the array of bytes, which must be
				 * >= 0. */
{
    ByteArray *byteArrayPtr;

    if (Tcl_IsShared(objPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayObj");
    }
    TclFreeIntRep(objPtr);
    Tcl_InvalidateStringRep(objPtr);

    length = (length < 0) ? 0 : length;
    byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
    byteArrayPtr->used = length;
    byteArrayPtr->allocated = length;
    if (bytes && length) {
	memcpy(byteArrayPtr->bytes, bytes, (size_t) length);
    }

    objPtr->typePtr = &tclByteArrayType;
    SET_BYTEARRAY(objPtr, byteArrayPtr);
}
======
These two simple changes make the routine idiot-proof --er-- error-proof, and improve the functionality to allow creating a new ByteArray object with an uninitialized 'length' of bytes:
  Tcl_Obj *myByteArray = Tcl_NewByteArray( NULL, 1024 );
  unsigned char *my1024 = Tcl_GetByteArrayFromObj( myByteArray, NULL );

Finally, I would have Tcl_GetByteArrayFromObj() return NULL if the ByteArray is zero-length.
======
unsigned char *
Tcl_GetByteArrayFromObj(
    Tcl_Obj *objPtr,		/* The ByteArray object. */
    int *lengthPtr)		/* If non-NULL, filled with length of the
				 * array of bytes in the ByteArray object. */
{
    ByteArray *baPtr;

    if (objPtr->typePtr != &tclByteArrayType) {
	SetByteArrayFromAny(NULL, objPtr);
    }
    baPtr = GET_BYTEARRAY(objPtr);

    if (lengthPtr != NULL) {
	*lengthPtr = baPtr->used;
    }
    if (!baPtr->bytes) {
	return NULL;
    }
    return (unsigned char *) baPtr->bytes;
}
======
But this is not essential... assuming the user is smart enough to actually verify that there is data before reading/writing it.

If you wan't I'll actually submit a patch, but as this only requires adding six lines...
======
[Alexandre Ferrieux] I think using NULL this way is really neat. But why bother with negative lengths at this point ? I mean, enforcing obvious contracts is an all-or-none decision. Checking all preconditions like that can lead to adding 10,000 lines of code to existing Tcl... It may be useful at some spots, because it allows early detection, but here it's just one nanosecond before ;-) Moreover, maybe in the future someone will find a neat semantics for negative values like you just did with the NULL pointer (eg to indicate a buffer to externally-allocated memory like an mmap(), that shouldn't be ckfree()ed on object disposal)...
----
!!!!!!
%| [Category Tcl Library] |%
!!!!!!} regexp2} CALL {my render Tcl_NewByteArrayObj {Tcl_Obj * '''Tcl_NewByteArrayObj'''(CONST unsigned char *''bytes'', int ''length'')

Tcl_Obj *
Tcl_NewByteArrayObj(bytes, length)

Tcl_NewByteArrayObj will create a new object of byte-array type.
Both of these procedures set the object's type to be byte-array and set the object's internal representation to a copy of the array of bytes given by bytes.
Tcl_NewByteArrayObj returns a pointer to a newly allocated object with a reference count of zero.

http://www.tcl.tk/man/tcl8.5/TclLib/ByteArrObj.htm
----
[HaO]: To populate a byte array by a C extension and return it as the result object, the following code might be used:

======
Tcl_Obj *pObj = Tcl_NewObj();
unsigned char *pChar = Tcl_SetByteArrayLength(pObj, 3);

// Dummy population functionality
pChar[0] = '\1'; pChar[1] = '\xff'; pChar[2] = '\x80';
Tcl_InvalidateStringRep(pObj);
Tcl_SetObjResult(interp,pObj); 
======
Remarks:
   * [Alexandre Ferrieux]: '''Tcl_NewObj'''() may be replaced by '''Tcl_NewByteArrayObj'''( "", 1 ).
   * [DGP]: If the object is not newly created (for example a parameter object is modified, one should check the object to be shared:
======
if ( Tcl_IsShared(pObj) )
    pObj = Tcl_DuplicateObj(pObj);
======

----
[DKF]: I don't recommend using a length of 0 to Tcl_NewByteArrayObj; the behaviour of that depends on whether malloc(0) keels over (which is system dependent).

[Duoas] That seems me fairly obnoxious. Doesn't the Tcl_NewByteArrayObj() code know that it oughtn't bother to try allocating zero bytes, and just initialize an empty/nil internal representation to begin with? [[edit2]] For that matter, would it be reasonable to specify NULL for source and a non-zero length for an uninitialized block of bytes?

[DKF]: I merely report the current state of affairs.

[Duoas] :-D  Perhaps I'll get the latest CVS and fix that sometime in the next couple of days.

[Duoas] 2008-10-31 Well, I've looked at the source (`~/tcl/generic/tclBinary.c`) and this is what I've found.

The documentation plainly states that 'length' must be greater than or equal to zero. Assuming no one ever passes a negative number, none of the functions will break. Zero is fine --malloc() will ''not'' get a zero size_t for a zero-length byte array, so it is malloc()-safe (remembering again not to give it negative values).

Frankly, I think I would insert a line above 327 that forces 'length' to be >= 0.

As for a NULL 'bytes' argument, if 'length' is zero, there is no problem. However, if 'length' is non-zero, then you will almost surely cause a segfault/access violation. Again, I would insert a little line above 330:331 just for that:
======
void
Tcl_SetByteArrayObj(
    Tcl_Obj *objPtr,		/* Object to initialize as a ByteArray. */
    const unsigned char *bytes,	/* The array of bytes to use as the new
				 * value. */
    int length)			/* Length of the array of bytes, which must be
				 * >= 0. */
{
    ByteArray *byteArrayPtr;

    if (Tcl_IsShared(objPtr)) {
	Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayObj");
    }
    TclFreeIntRep(objPtr);
    Tcl_InvalidateStringRep(objPtr);

    length = (length < 0) ? 0 : length;
    byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length));
    byteArrayPtr->used = length;
    byteArrayPtr->allocated = length;
    if (bytes && length) {
	memcpy(byteArrayPtr->bytes, bytes, (size_t) length);
    }

    objPtr->typePtr = &tclByteArrayType;
    SET_BYTEARRAY(objPtr, byteArrayPtr);
}
======
These two simple changes make the routine idiot-proof --er-- error-proof, and improve the functionality to allow creating a new ByteArray object with an uninitialized 'length' of bytes:
  Tcl_Obj *myByteArray = Tcl_NewByteArray( NULL, 1024 );
  unsigned char *my1024 = Tcl_GetByteArrayFromObj( myByteArray, NULL );

Finally, I would have Tcl_GetByteArrayFromObj() return NULL if the ByteArray is zero-length.
======
unsigned char *
Tcl_GetByteArrayFromObj(
    Tcl_Obj *objPtr,		/* The ByteArray object. */
    int *lengthPtr)		/* If non-NULL, filled with length of the
				 * array of bytes in the ByteArray object. */
{
    ByteArray *baPtr;

    if (objPtr->typePtr != &tclByteArrayType) {
	SetByteArrayFromAny(NULL, objPtr);
    }
    baPtr = GET_BYTEARRAY(objPtr);

    if (lengthPtr != NULL) {
	*lengthPtr = baPtr->used;
    }
    if (!baPtr->bytes) {
	return NULL;
    }
    return (unsigned char *) baPtr->bytes;
}
======
But this is not essential... assuming the user is smart enough to actually verify that there is data before reading/writing it.

If you wan't I'll actually submit a patch, but as this only requires adding six lines...
======
[Alexandre Ferrieux] I think using NULL this way is really neat. But why bother with negative lengths at this point ? I mean, enforcing obvious contracts is an all-or-none decision. Checking all preconditions like that can lead to adding 10,000 lines of code to existing Tcl... It may be useful at some spots, because it allows early detection, but here it's just one nanosecond before ;-) Moreover, maybe in the future someone will find a neat semantics for negative values like you just did with the NULL pointer (eg to indicate a buffer to externally-allocated memory like an mmap(), that shouldn't be ckfree()ed on object disposal)...
----
!!!!!!
%| [Category Tcl Library] |%
!!!!!!}} CALL {my revision Tcl_NewByteArrayObj} CALL {::oo::Obj4058496 process revision/Tcl%5FNewByteArrayObj} CALL {::oo::Obj4058494 process}

-errorcode

NONE

-errorinfo

Unknow state transition: LINE -> END
    while executing
"error $msg"
    (class "::Wiki" method "render_wikit" line 6)
    invoked from within
"my render_$default_markup $N $C $mkup_rendering_engine"
    (class "::Wiki" method "render" line 8)
    invoked from within
"my render $name $C"
    (class "::Wiki" method "revision" line 31)
    invoked from within
"my revision $page"
    (class "::Wiki" method "process" line 56)
    invoked from within
"$server process [string trim $uri /]"

-errorline

4