• Welcome to Valhalla Legends Archive.
 

[C#] Code submission - DebugOutput

Started by TehUser, June 16, 2005, 03:41 PM

Previous topic - Next topic

TehUser

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


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                                            ...............



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;
}

MyndFyre

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:

if (((char.IsLetterOrDigit(c) || char.IsSymbol(c) || char.IsPunctuation(c)) || (char.IsSeparator(c) || char.IsWhiteSpace(c))) && !char.IsControl(c))


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!
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

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:


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();
}

MyndFyre

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.

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*
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

l)ragon

Quote from: TehUser on June 16, 2005, 07:12 PM

public String DebugOutput(byte[] bytesIn, int byteLength)
{
//*** CHOPED ***//
}

you dont realy need to send the function the length either when you can get it like this -> bytesIn.Length
*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*ˆ¨¯¯¨ˆ*^~·.,l)ragon,.-·~^*ˆ¨¯¯¨ˆ*^~·.,¸¸,.·´¯`·.,¸¸,.-·~^*

MyndFyre

Quote from: l)ragon on June 17, 2005, 12:44 AM
Quote from: TehUser on June 16, 2005, 07:12 PM

public String DebugOutput(byte[] bytesIn, int byteLength)
{
//*** CHOPED ***//
}

you dont realy need to send the function the length either when you can get it like this -> bytesIn.Length

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.  :)
QuoteEvery generation of humans believed it had all the answers it needed, except for a few mysteries they assumed would be solved at any moment. And they all believed their ancestors were simplistic and deluded. What are the odds that you are the first generation of humans who will understand reality?

After 3 years, it's on the horizon.  The new JinxBot, and BN#, the managed Battle.net Client library.

Quote from: chyea on January 16, 2009, 05:05 PM
You've just located global warming.

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?