Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There are a couple of edge-cases which make this a neat problem:</p> <ul> <li>the input array might be empty</li> <li>the last and next-to-last bits need to be treated specially, because they have zero bits shifted into them</li> </ul> <p>Here's a simple solution which loops over the array copying the low-order nibble of the next byte into its high-order nibble, and the high-order nibble of the next-next (+2) byte into its low-order nibble. To save dereferencing the look-ahead pointer twice, it maintains a two-element buffer with the "last" and "next" bytes: </p> <pre><code>void shl12(uint8_t *v, size_t length) { if (length == 0) { return; // nothing to do } if (length &gt; 1) { uint8_t last_byte, next_byte; next_byte = *(v + 1); for (size_t i = 0; i + 2 &lt; length; i++, v++) { last_byte = next_byte; next_byte = *(v + 2); *v = ((last_byte &amp; 0x0f) &lt;&lt; 4) | (((next_byte) &amp; 0xf0) &gt;&gt; 4); } // the next-to-last byte is half-empty *(v++) = (next_byte &amp; 0x0f) &lt;&lt; 4; } // the last byte is always empty *v = 0; } </code></pre> <p>Consider the boundary cases, which activate successively more parts of the function:</p> <ul> <li>When <code>length</code> is zero, we bail out without touching memory.</li> <li>When <code>length</code> is one, we set the one and only element to zero.</li> <li>When <code>length</code> is two, we set the high-order nibble of the first byte to low-order nibble of the second byte (that is, bits 12-16), and the second byte to zero. We don't activate the loop.</li> <li>When <code>length</code> is greater than two we hit the loop, shuffling the bytes across the two-element buffer.</li> </ul> <p>If efficiency is your goal, the answer probably depends largely on your machine's architecture. Typically you should maintain the two-element buffer, but handle a machine word (32/64 bit unsigned integer) at a time. If you're shifting a lot of data it will be worthwhile treating the first few bytes as a special case so that you can get your machine word pointers word-aligned. Most CPUs access memory more efficiently if the accesses fall on machine word boundaries. Of course, the trailing bytes have to be handled specially too so you don't touch memory past the end of the array.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload