Valhalla Legends Forums Archive | .NET Platform | [C#] Code submission - DebugOutput

AuthorMessageTime
TehUser
Styled after Grok's original VB DebugOutput, this function copies the contents of a byte[] into the following format:

[code]
0000:  49 20 6C 69 6B 65 20 74 6F 20 73 65 6E 64 20 6D  I like to send m
0010:  6F 72 65 20 64 61 74 61 2E 0D 0A 41 6E 64 20 6D  ore data...And m
0020:  6F 72 65 20 61 6E 64 20 6D 6F 72 65 20 61 6E 64  ore and more and
0030:  20 6D 6F 72 65 20 61 6E 64 20 6D 6F 72 65 20 61   more and more a
0040:  6E 64 20 6D 6F 72 65 21 0D 0A 44 61 74 61 20 69  nd more!..Data i
0050:  73 20 6D 79 20 66 72 69 65 6E 64 2E 20 20 3D 44  s my friend.  =D
0060:  0D 0A 4C 61 20 6C 61 20 6C 61 20 6C 61 20 6C 61  ..La la la la la
0070:  20 6C 61 20 6C 61 20 6C 61 20 6C 61 20 6C 61 2E   la la la la la.
0080:  2E 2E                                            ...............
[/code]

[code]
public String DebugOutput(byte[] bytesIn, int byteLength)
{
String strOut = "";
String strText = "", strBytes = "";

for (int i = 0; i < byteLength; i++)
{
if ((i % 16) == 0) strOut += String.Format("{0:X4}:  ", i);

strBytes += String.Format("{0:X2} ", bytesIn[i]);

switch (bytesIn[i])
{
case 0:
case 9:
case 10:
case 13:
strText += ".";
break;
default:
strText += (char)bytesIn[i];
break;
}

if (((i+1) % 16 == 0) || (i == byteLength - 1))
{
if (strBytes.Length < 48) strBytes = strBytes.PadRight(48, ' ');
if (strText.Length < 16) strText = strText.PadRight(16, '.');
strOut += strBytes + " " + strText + (i == (byteLength - 1) ? ((char)bytesIn[i]).ToString() : "") + "\n";
strBytes = "";
strText = "";
}
}

strOut = strOut.Remove(strOut.Length - 2); // Remove the last newline.
return strOut;
}
[/code]
June 16, 2005, 8:41 PM
Myndfyr
May I suggest a few things?

1.) Use a StringBuilder object instead of string and concat.  A StringBuilder is a dynamically-allocated, mutable string and should achieve a significant performance boost over a standard string (as strings are immutable -- once you've activated a string in memory, it stays there until the application terminates).
2.) Instead of using a switch/case construction, use the char-type static methods to determine if it's printable:
[code]
if (((char.IsLetterOrDigit(c) || char.IsSymbol(c) || char.IsPunctuation(c)) || (char.IsSeparator(c) || char.IsWhiteSpace(c))) && !char.IsControl(c))[/code]

If that condition is met, then you should be able to print the character; otherwise, print ".".  That way, you can display nonregional characters as well.

3.) This isn't necessary, but more of a style issue: instead of using String.Format("{0:x4}"), consider using byteInstance.ToString("x2") (or "x4").  That way it's more clear what you're formatting and how.  Note that it's most appropriate to format a byte with x2, a short with x4, an int with x8, and a long with x16.

Cheers!
June 16, 2005, 10:07 PM
TehUser
Ah, thanks, I was hoping someone would see some ways to optimize it.  Quick thing though, the immutability of a string just means that its contents are read-only and the string must be copied any time it is modified.  I would think (unless I'm mistaken, which may be the case) that strings that are no longer referenced would be garbage collected during the runtime of the application and would not, as you said, "stay there until the application terminates".

Anyhow, here's the updated one:

[code]
public String DebugOutput(byte[] bytesIn, int byteLength)
{
StringBuilder strOut = new StringBuilder("");
StringBuilder strText = new StringBuilder(16);
StringBuilder strBytes = new StringBuilder(48);

for (int i = 0; i < byteLength; i++)
{
if ((i % 16) == 0) strOut.Append(i.ToString("X4") + ":  ");

strBytes.Append(bytesIn[i].ToString("X2") + " ");

char c = (char)bytesIn[i];
if (char.IsControl(c))
strText.Append( ".");
else
strText.Append( c);

if (((i+1) % 16 == 0) || (i == byteLength - 1))
{
if (strBytes.Length < 48) strBytes.Append(' ', 48 - strBytes.Length);
if (strText.Length < 16) strText.Append('.', 16 - strText.Length);
strOut.Append( strBytes + " " + strText + (i == (byteLength - 1) ? ((char)bytesIn[i]).ToString() : "") + "\n");
strBytes.Remove(0, strBytes.Length);
strText.Remove(0, strText.Length);
}
}

strOut = strOut.Remove(strOut.Length - 2, 2); // Remove the last newline.
return strOut.ToString();
}[/code]
June 17, 2005, 12:12 AM
Myndfyr
Okay, that wasn't ENTIRELY accurate.  Here's how it works:

From MSDN:
[quote]
The problem with the BuildXml1 code lies in the fact that the System.String data type exposed by the .NET Framework represents an immutable string. This means that every time the string data is changed, the original representation of the string in memory is destroyed and a new one is created containing the new string data, resulting in a memory allocation operation and a memory de-allocation operation. Of course, this is all taken care of behind the scenes, so the true cost is not immediately apparent. Allocating and de-allocating memory causes increased activity related to memory management and garbage collection within the Common Language Runtime (CLR) and thus can be expensive.
[/quote]

The rest of it lies within the CLR itself.  When a string is deallocated, it isn't immediately destroyed by the CLR if the CLR thinks that it could be used again.  I don't really know how to explain this short of giving an example.

Let's say you're using the string "TehUser" several times within your application.  That string is actually only represented once in memory, and each time it's used, you have a reference to that particular string (which is why the string is immutable).  It can't be destroyed at all until all references to that particular memory are out of scope, and even then, the CLR might choose *not* to delete it and then, when necessary, give a reference to that memory back to something else (this may be true if that memory was used extensively before).

I wish I could remember what this method of string handling was called, but offhand I can't.  *shrug*
June 17, 2005, 12:39 AM
dRAgoN
[quote author=TehUser link=topic=11860.msg116134#msg116134 date=1118967152]
[code]
public String DebugOutput(byte[] bytesIn, int byteLength)
{
//*** CHOPED ***//
}[/code]
[/quote]
you dont realy need to send the function the length either when you can get it like this -> bytesIn.Length
June 17, 2005, 5:44 AM
Myndfyr
[quote author=l)ragon link=topic=11860.msg116187#msg116187 date=1118987085]
[quote author=TehUser link=topic=11860.msg116134#msg116134 date=1118967152]
[code]
public String DebugOutput(byte[] bytesIn, int byteLength)
{
//*** CHOPED ***//
}[/code]
[/quote]
you dont realy need to send the function the length either when you can get it like this -> bytesIn.Length
[/quote]

True enough.  Bear in mind though that the function might not need to format *all* the bytes in that array (a lot of byte-array-related functions in the Framework follow the pattern (array, startIndex, Length) in their argument list.  It's good to be consistent.  :)
June 17, 2005, 7:47 AM
TehUser
I typically pass in the byte array directly from what my server receives, which is a 1024-byte buffer.  bytesIn.Length always returns 1024 because that's the length of the array, but without passing in a data length, it will display all 1024 bytes.

On another note, I'm having some problems with disconnecting clients from my server.  I shutdown the socket, call BeginDisconnect, get the callback, call EndDisconnect, and the client socket closes.  However, the client is still in CLOSE_WAIT and the server is in FIN_WAIT2.  Does anyone know how to actually get the connection to end?
June 17, 2005, 2:40 PM

Search