|
| Why is BinaryWriter.Write7BitEncodedInt a protected method? |
|
|
|
|
| Messages |
|
Related Types |
This message was discovered on microsoft.public.dotnet.framework.performance.
Responses highlighted in red are from those people who are likely to be able to contribute good, authoratitive information to this discussion. They include Microsoft employees, MVP's and others who IMHO contribute well to these kinds of discussions.
| Chris |
| GOOD ANSWER |
Why is the BinaryWriter.Write7BitEncodedInt a protected method? I wish to use this functionality out of the box, yet I must override the BinaryWriter class to do so, just curious why Microsoft?
Thanks in advance.
-Chris
|
|
|
| |
|
|
| |
| |
| Brian Grunkemeyer (VIP) |
| GOOD ANSWER |
We wrote this method as an internal implementation detail for the Write(String) method. BinaryWriter prefixes strings with the number of bytes, and we want a pretty compact representation of that length, since a lot of strings are short. So we added this method. We didn't believe it needed to be publically exposed, but we'll look at doing this in a future release.
In the mean time, here's the implementation. It's a little tricky to get right, at least if you want to support negative numbers, or if your "byte" keyword suddenly changes from signed to unsigned as your product evolves. Hopefully this will help you out.
protected void Write7BitEncodedInt(int value) { // Write out an int 7 bits at a time. The high bit of the byte, // when on, tells reader to continue reading more bytes. uint v = (uint) value; // support negative numbers while (v >= 0x80) { Write((byte) (v | 0x80)); v >>= 7; } Write((byte)v); }
protected int Read7BitEncodedInt() { // Read out an int 7 bits at a time. The high bit // of the byte when on means to continue reading more bytes. int count = 0; int shift = 0; byte b; do { b = ReadByte(); count |= (b & 0x7F) << shift; shift += 7; } while ((b & 0x80) != 0); return count; }
Brian Grunkemeyer MS CLR Base Class Library team
|
|
|
| |
|
|
| |
| |
| Chris Tanger |
| GOOD ANSWER |
Sorry for the delay, you were probably wondering if your answer was even read. My computer hard disk crashed, it took me a while to rebuild, and I was unabled to import my backed up news watch lists.
Thanks for the answer. I am writing some of my own Stream components and needed the compressed int functionality for the same reasons you elected to use it. I would also be *very* interested in a compressed double and decimal. I believe using the code you provided I should be able to create a compressed Int64 rather easily. The double would be rather complex due to the way mantissa and exponents are stored internally. I think a decimal shouldn't be too difficult though.
Thanks,
-Chris Tanger
"Brian Grunkemeyer" <Click here to reveal e-mail address> wrote in message news:Click here to reveal e-mail address... [Original message clipped]
|
|
|
| |
|
|
| |
| |
| Justin Rogers |
| GOOD ANSWER |
Note Brian's code doesn't have any sort of stream corruption protection. In fact there is a way to hang a call to Read7Bit... by simply having a bunch of bytes in your file with high order bits set.
http://weblogs.asp.net/justin_rogers/archive/2004/02/19/76709.aspx" target="_blank">http://weblogs.asp.net/justin_rogers/archive/2004/02/19/76709.aspx http://weblogs.asp.net/justin_rogers/archive/2004/02/21/77561.aspx
Above is the result of an examination of the function in question. I think there may also be a link to the initial examination. Because of the encoding methods used it is possible that other API's reliant on using the compression technique can also hang.
I will say in the below code, that there is a fix for the negative number issue that I originally found in the rotor source, but still no shift register checking for out of bounds numbers.
-- Justin Rogers DigiTec Web Consultants, LLC. Blog: http://weblogs.asp.net/justin_rogers
"Chris Tanger" <Click here to reveal e-mail address> wrote in message news:Click here to reveal e-mail address... [Original message clipped]
|
|
|
| |
|
|
| |
| |
| Chris Tanger |
| GOOD ANSWER |
Yes, I did notice the lack of out of bounds checking, that did surprise me considering the sometimes excessive checking done in other .NET classes in safer contexts. I missed the inability to properly handle negative numbers, that is a big one, thanks for the heads-up.
-Chris Tanger
"Justin Rogers" <Click here to reveal e-mail address> wrote in message news:Click here to reveal e-mail address... > Note Brian's code doesn't have any sort of stream corruption protection. In [Original message clipped]
|
|
|
| |
|
|
| |
|
| |
| Brian Grunkemeyer (VIP) |
| GOOD ANSWER |
Fortunately, the code I posted had the fix for the negative number problem (even though in our current usage, it's impossible to pass a negative number to that method). I made this fix in our Whidbey tree quite a while back.
I think Justin's code is fine for Justin's needs. One problem I ran into when looking into this was that Read7BitEncodedInt was calling BinaryReader's ReadByte, which is virtual. While ReadByte does do the error checking for the end of the stream (which you probably thought I left out), it does a lot of work to get the results you want. I don't know of anyone who has attempted to subclass BinaryReader & override all the methods, but perhaps we shouldn't break them if they exist. So I sped up ReadByte a bit to avoid an extra method call & other branches as well (not shown). You can look for this wherever you get the Whidbey Beta 2 sources in a few months.
But the check for a corrupted input stream is interesting. I'll spend some more time looking at this later, but here's what I came up with:
internal protected int Read7BitEncodedInt() { // Read out an Int32 7 bits at a time. The high bit // of the byte when on means to continue reading more bytes. int count = 0; int shift = 0; byte b; do { // Check for a corrupted stream. Read a max of 5 bytes. // @TODO: In a future version, add a DataFormatException. if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7 throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32"));
// ReadByte handles end of stream cases for us. b = ReadByte(); count |= (b & 0x7F) << shift; shift += 7; } while ((b & 0x80) != 0); return count; }
Elsewhere, I've added this:
Format_Bad7BitInt32 = Too many bytes in what should have been a 7 bit encoded Int32.
We should add a DataFormatException class that subclasses FormatException. (It's not an IOException - you didn't have a disk failure, and we found the file. Instead, it was corrupted or it wasn't the file you thought it was.) I can't promise we'll add that in Whidbey, but I'll run it by a few people. Note it's not a breaking change to move from throwing an exception to throwing a subclass of that exception.
Brian Grunkemeyer MS CLR Base Class Library team
|
|
|
| |
|
|
| |
|
|
|
|
|
|
|
|
|
|
|
BootFX
Reliable and powerful .NET application framework. |
|
|
|
|
|
|