@@ -252,19 +252,81 @@ public void WriteCompressedUInt32 (uint value)
252
252
253
253
public void WriteCompressedInt32 ( int value )
254
254
{
255
- if ( value >= 0 ) {
256
- WriteCompressedUInt32 ( ( uint ) ( value << 1 ) ) ;
257
- return ;
255
+ // extracted from System.Reflection.Metadata
256
+ unchecked {
257
+ const int b6 = ( 1 << 6 ) - 1 ;
258
+ const int b13 = ( 1 << 13 ) - 1 ;
259
+ const int b28 = ( 1 << 28 ) - 1 ;
260
+
261
+ // 0xffffffff for negative value
262
+ // 0x00000000 for non-negative
263
+
264
+ int signMask = value >> 31 ;
265
+
266
+ if ( ( value & ~ b6 ) == ( signMask & ~ b6 ) ) {
267
+ int n = ( ( value & b6 ) << 1 ) | ( signMask & 1 ) ;
268
+ WriteByte ( ( byte ) n ) ;
269
+ } else if ( ( value & ~ b13 ) == ( signMask & ~ b13 ) ) {
270
+ int n = ( ( value & b13 ) << 1 ) | ( signMask & 1 ) ;
271
+ ushort val = ( ushort ) ( 0x8000 | n ) ;
272
+ WriteUInt16 ( BitConverter . IsLittleEndian ? ReverseEndianness ( val ) : val ) ;
273
+ } else if ( ( value & ~ b28 ) == ( signMask & ~ b28 ) ) {
274
+ int n = ( ( value & b28 ) << 1 ) | ( signMask & 1 ) ;
275
+ uint val = 0xc0000000 | ( uint ) n ;
276
+ WriteUInt32 ( BitConverter . IsLittleEndian ? ReverseEndianness ( val ) : val ) ;
277
+ } else {
278
+ throw new ArgumentOutOfRangeException ( "value" , "valid range is -2^28 to 2^28 -1" ) ;
279
+ }
258
280
}
281
+ }
282
+
283
+ static uint ReverseEndianness ( uint value )
284
+ {
285
+ // extracted from .net BCL
286
+
287
+ // This takes advantage of the fact that the JIT can detect
288
+ // ROL32 / ROR32 patterns and output the correct intrinsic.
289
+ //
290
+ // Input: value = [ ww xx yy zz ]
291
+ //
292
+ // First line generates : [ ww xx yy zz ]
293
+ // & [ 00 FF 00 FF ]
294
+ // = [ 00 xx 00 zz ]
295
+ // ROR32(8) = [ zz 00 xx 00 ]
296
+ //
297
+ // Second line generates: [ ww xx yy zz ]
298
+ // & [ FF 00 FF 00 ]
299
+ // = [ ww 00 yy 00 ]
300
+ // ROL32(8) = [ 00 yy 00 ww ]
301
+ //
302
+ // (sum) = [ zz yy xx ww ]
303
+ //
304
+ // Testing shows that throughput increases if the AND
305
+ // is performed before the ROL / ROR.
306
+
307
+ return RotateRight ( value & 0x00FF00FFu , 8 ) // xx zz
308
+ + RotateLeft ( value & 0xFF00FF00u , 8 ) ; // ww yy
309
+ }
310
+
311
+ // extracted from .net BCL
312
+ static uint RotateRight ( uint value , int offset )
313
+ => ( value >> offset ) | ( value << ( 32 - offset ) ) ;
314
+
315
+ static uint RotateLeft ( uint value , int offset )
316
+ => ( value << offset ) | ( value >> ( 32 - offset ) ) ;
317
+
318
+ static ushort ReverseEndianness ( ushort value )
319
+ {
320
+ // extracted from .net BCL
259
321
260
- if ( value > - 0x40 )
261
- value = 0x40 + value ;
262
- else if ( value >= - 0x2000 )
263
- value = 0x2000 + value ;
264
- else if ( value >= - 0x20000000 )
265
- value = 0x20000000 + value ;
322
+ // Don't need to AND with 0xFF00 or 0x00FF since the final
323
+ // cast back to ushort will clear out all bits above [ 15 .. 00 ].
324
+ // This is normally implemented via "movzx eax, ax" on the return.
325
+ // Alternatively, the compiler could elide the movzx instruction
326
+ // entirely if it knows the caller is only going to access "ax"
327
+ // instead of "eax" / "rax" when the function returns.
266
328
267
- WriteCompressedUInt32 ( ( uint ) ( ( value << 1 ) | 1 ) ) ;
329
+ return ( ushort ) ( ( value >> 8 ) + ( value << 8 ) ) ;
268
330
}
269
331
270
332
public void WriteBytes ( byte [ ] bytes )
0 commit comments