I found one project where I can't uderstand one piece of code.Virtual memory
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define TLB_SIZE 16
#define PAGE_SIZE 256
#define FRAME_SIZE 256
#define PHYSICAL_MEMORY_SIZE PAGE_SIZE *FRAME_SIZE
int logicalAddress = 0;
int offsetNumber = 0;
int pageNumber = 0;
int physicalAddress = 0;
int Frame = 0;
int Value = 0;
int Hit = 0;
int tlbIndex = 0;
int tlbSize = 0;
unsigned pageNumberMask = 65280; // 1111111100000000
unsigned offsetMask = 255; // 11111111
int tlbHitCount = 0;
float tlbHitRate = 0;
int addressCount = 0;
int pageFaultCount = 0;
float pageFaultRate = 0;
struct tlbTable {
unsigned int pageNum;
unsigned int frameNum;
};
int main(int argc, char *argv[]) {
// Check to see if user inputs addresses.txt
if (argc != 2) {
fprintf(stderr, "Usage ./VirtualMem_Manager <Filename.txt> \n");
exit(1);
}
// Open addresses.txt, BACKING_STORE.bin, and
// Create Output.txt to store program results
FILE *addresses = fopen(argv[1], "r");
FILE *BACKINGSTORE = fopen("BACKING_STORE.bin", "rb");
FILE *Output = fopen("addressOutput.txt", "w");
int physicalMemory[PHYSICAL_MEMORY_SIZE];
char Buffer[256];
int Index;
// Declare and initialize pageTable[] array to -1
int pageTable[PAGE_SIZE];
memset(pageTable, -1, 256 * sizeof(int));
// Declare and initialize tlb[] structure to -1
struct tlbTable tlb[TLB_SIZE];
memset(pageTable, -1, 16 * sizeof(char));
// Read each address from addresses.txt
while (fscanf(addresses, "%d", &logicalAddress) == 1) {
addressCount++;
// set the page number and offset for each logical address
pageNumber = logicalAddress & pageNumberMask;
pageNumber = pageNumber >> 8;
offsetNumber = logicalAddress & offsetMask;
Hit = -1;
// Check to see if the page number is already in the tlb
// If it is in tlb, then it is tlb hit
for (Index = 0; Index < tlbSize; Index++) {
if (tlb[Index].pageNum == pageNumber) {
Hit = tlb[Index].frameNum;
physicalAddress = Hit * 256 + offsetNumber;
}
}
if (!(Hit == -1)) {
tlbHitCount++;
}
// This "else if" loop is the tlb miss
// Gets the physical page number from page table
else if (pageTable[pageNumber] == -1) {
fseek(BACKINGSTORE, pageNumber * 256, SEEK_SET);
fread(Buffer, sizeof(char), 256, BACKINGSTORE);
pageTable[pageNumber] = Frame;
for (Index = 0; Index < 256; Index++) {
physicalMemory[Frame * 256 + Index] = Buffer[Index];
}
pageFaultCount++;
Frame++;
// FIFO algorithm for the tlb
if (tlbSize == 16) tlbSize--;
for (tlbIndex = tlbSize; tlbIndex > 0; tlbIndex--) {
tlb[tlbIndex].pageNum = tlb[tlbIndex - 1].pageNum;
tlb[tlbIndex].frameNum = tlb[tlbIndex - 1].frameNum;
}
if (tlbSize <= 15) tlbSize++;
tlb[0].pageNum = pageNumber;
tlb[0].frameNum = pageTable[pageNumber];
physicalAddress = pageTable[pageNumber] * 256 + offsetNumber;
} else {
physicalAddress = pageTable[pageNumber] * 256 + offsetNumber;
}
// Gets the value from the bin file provided
Value = physicalMemory[physicalAddress];
// print the addresses and value to Output.txt
fprintf(Output, "Virtual Address: %d Physical Address: %d Value: %d \n",
logicalAddress, physicalAddress, Value);
}
// The statistics of the program
pageFaultRate = pageFaultCount * 1.0f / addressCount;
tlbHitRate = tlbHitCount * 1.0f / addressCount;
// Close files provided for the project
fclose(addresses);
fclose(BACKINGSTORE);
// Print the statistics of the program to Output.txt
fprintf(Output, "Number of Addresses: %d\n", addressCount);
fprintf(Output, "Number of Page Faults: %d\n", pageFaultCount);
fprintf(Output, "Page Fault Rate: %f\n", pageFaultRate);
fprintf(Output, "TLB Hits: %d\n", tlbHitCount);
fprintf(Output, "TLB Hit Rate %f\n", tlbHitRate);
// Close Output.txt
fclose(Output);
return 0;
}
I can't understand these three lines.
pageNumber = logicalAddress & pageNumberMask;
pageNumber = pageNumber >> 8;
offsetNumber = logicalAddress & offsetMask;
As I understand papeNumber is value where logicalAddress and pageNuberMask a stored together. And this char & join them. Then we do offset on 8(>> 8).
And why is using memset dangerous.
(For purposes of illustration, this answer assumes the addresses are 32 bits, which appears to be consistent with the code in the question.)
pageNumber = logicalAddress & pageNumberMask;
pageNumber = pageNumber >> 8;
offsetNumber = logicalAddress & offsetMask;
The logical address is partitioned into three sets of bits: 16 upper bits that are ignored in this code, 8 bits used for a page number, and 8 lower bits that are used for an offset within a page.
pageNumberMask is a value that has been prepared to have 1s in the page number bits and 0s elsewhere. Then the operation logicalAddress & pageNumberMask performs a bitwise AND, which produces a value in which the only bits set are those that were set in the page number bits of logicalAddress. In other words, it produces a value in which the upper bits and the offset bits are cleared, effectively isolating the page number bits.
Then pageNumber >> 8 shifts those page number bits down to the low bits, so the result is a page number. That page number can then be used to index arrays.
Similarly, offsetMask is a prepared value with 1s in the offset bits and 0s elsewhere, and logicalAddress & offsetMask extracts just the offset bits.
Note that logicalAddress & pageNumberMask serves a purpose only if bits above the page number bits might be set. This is because pageNumber >> 8 removes the lower bits anyway, so there is no point in clearing them with an & operation. So this code separates the page number bits from the upper bits but makes no use of those upper bits. This might be because it is a simplified example for teaching, and actual code for deployment would make use of the upper bits.
And why is using memset dangerous.
memset is a powerful routine that is easy to use incorrectly. Given incorrect arguments, it will modify any accessible memory, and the compiler often cannot detect that it has been used incorrectly, so it cannot provide a warning or error message.
Defects in the Code
The code in the question has a number of defects, so it is not a great example for teaching:
struct tlbTable tlb[TLB_SIZE]; followed by memset(pageTable, -1, 16*sizeof(char)); looks like a bug. pageTable was initialized previously, and this was probably intended to initialize tlb. And the size is wrong; tlb has 16 elements (TLB_SIZE is 16), and each is several bytes. That line should probably be memset(tlb, -1, sizeof tlb);, and the previous memset should be memset(pageTable, -1, sizeof pageTable);.
Preprocessor macros are defined for various parameters, such as 256 for PAGE_SIZE, but then parts of the code ignore these and use hard-coded constants, as in Frame*256 + Index. This is a recipe for bugs.
For performance and memory reasons pageNumber and offset in the page are stored in single unsigned value.
I will use c++14 bit representation with 0b prefix in comments to explain.
unsigned logicalAddress = someValue; // <- 0bPPPPPPPPFFFFFFFF
where P denotes bits for pageNumber and F bits for offsetNumber;
pageNumber = logicalAddress & pageNumberMask; // <- 0bPPPPPPPP00000000
pageNumber = pageNumber >> 8; // <- 0bPPPPPPPP
offsetNumber = logicalAddress & offsetMask; // <- 0bFFFFFFFF
About memset using it is not dangerous if used correctly.
Related
Image
hello, i have a list of locations as described in the image stored in a linked list. every node has an unsigned char in the size of 2(chessPos in the code) - the first location represents a row and the second a col. for example the first node: row = 'C', col = '5' and so on. the list is passed through the function i dont need to built it.
i need to write the data to a binary file, when each row or col is written in 3 bits. so 'C' will be written as 010 and right after '5' will be written as 100 (the 3 bits written represent the row/col -1, thats why '5' is represnted by 100 which is 4 in binary).
the difficulty is that every byte is 8 bits and every time i write a byte to the file it contains 6 bits which represt a row and a col, and 2 bits of the next byte.
how can i make it work?
thanks
this is my code so far:
typedef char chessPos[2];
typedef struct _chessPosArray {
unsigned int size;
chessPos* positions;
}chessPosArray;
typedef struct _chessPosCell {
chessPos position;
struct _chessPosCell* next;
}chessPosCell;
typedef struct _chessPosList {
chessPosCell* head;
chessPosCell* tail;
}chessPosList;
void function_name(char* file_name, chessPosList* pos_list)
{
FILE* file;
short list_len;
int i = 0;
unsigned char row, col, byte_to_file, next_byte;
chessPosCell* curr = pos_list->head;
file = fopen(file_name, "wb"); /* open binary file to writing */
checkFileOpening(file);
while (curr != NULL)
{
row = curr->position[0] - 'A' - 17; /* 'A' ---> '1' ---> '0' */
col = curr->position[1] - 1; /* '4' ---> '3' */
if (remain < 6)
{
curr = curr->next;
remain += 8;
}
if (i > 1)
{
i = 0;
}
if (curr->next != NULL)
{
next_byte = curr->next->position[i] >> (remain - 7);
byte_to_file = ((row << (remain - 3)) | (col << (remain - 6))) | (next_byte);
i++;
}
else
{
byte_to_file = ((row << (remain - 3)) | (col << (remain - 6)));
}
fwrite(&byte_to_file, sizeof(unsigned char), 1, file);
remain -= 6;
}
how can i make it work?
Since each location requires both a column and a row, you can actually think of a location as a single 6-bit value in which the lowest 3 bits are the row and the high 3 bits are the column. If you think of it that way, then the problem is a little bit simpler in that you're actually just talking about base-64 encoding/decoding, and there are lots of open-source implementations available if you really want to pack the data into the smallest possible space.
That said, I'd encourage you to consider whether your problem really requires minimizing the storage space. You could instead store those locations as characters, either using 4 bits for row and 4 for column, continue treating locations as 6-bit values and just ignore the two extra bits. Unless you're storing a huge number of these locations, the benefit of saving two bits per location isn't likely to matter.
how can i make it work?
Well, first start with a good abstraction. Anyway, it's actually pretty simple:
let's take a 16-bit/2-byte buffer and a bit position within the buffer
it's way easier when the buffer is continues (uint16_t) instead of two separate bytes (unsigned char byte_to_file, next_byte). The next_byte bits just shift themselves and byte_to_file can be extracted with a mask.
I "see" in my imagination MSB on the left and LSB on the right
for each new 6-bits push it to the most left position that is not set yet
so shift of 16-6 minus the position
if we filled more then 8 bits
take one byte and output it
and shift the buffer 8 bits to the left
Here's a sample program that prints Hello world\n:
#include <stdint.h>
#include <stdio.h>
struct bitwritter {
FILE *out;
// our buffer for bits
uint16_t buf;
// the count of set bits within buffer counting from MSB
unsigned char pos;
};
struct bitwritter bitwritter_init(FILE *out) {
return (struct bitwritter){ .out = out };
}
int bitwritter_write_6bits(struct bitwritter *t, unsigned char bits6) {
// we always write starting from MSB
unsigned char toshift = 16 - 6 - t->pos;
// just a mask with 6 bits
bits6 &= 0x3f;
t->buf |= bits6 << toshift;
t->pos += 6;
// do we have whole byte?
if (t->pos >= 8) {
// extract the byte - note it's in MSB
unsigned char towrite = t->buf >> 8;
// shift out buffer
t->buf <<= 8;
t->pos -= 8;
// write output
if (fwrite(&towrite, sizeof(towrite), 1, t->out) != 1) {
return -1;
}
return 1;
}
return 0;
}
int main() {
struct bitwritter bw = bitwritter_init(stdout);
// echo 'Hello world' | xxd -c1 -p | while read l; do python -c "print(\"{0:08b}\".format(0x$l))"; done | paste -sd '' | sed -E 's/.{6}/0b&,\n/g'
unsigned char data[] = {
0b010010,
0b000110,
0b010101,
0b101100,
0b011011,
0b000110,
0b111100,
0b100000,
0b011101,
0b110110,
0b111101,
0b110010,
0b011011,
0b000110,
0b010000,
0b001010,
};
for (size_t i = 0; i < sizeof(data); ++i) {
bitwritter_write_6bits(&bw, data[i]);
}
}
I have some billions of bits loaded into RAM by the use of malloc() - will call it big_set. I also have another amount of bits (will call it small_set) in RAM which are all set to 1 and I know its size (how many bits - I will call it ss_size), but can't predict it, as varies on each execution. ss_size can be sometimes as small as 100 or large as hundreds of millions.
I need to do some bitwise operations between small_set and some unpredictable parts of big_set of ss_size bits length. I can't just extend small_set with zeros on both most-significant and least-significant sides to make its size equal big_set's size, as that would be very RAM and CPU expensive (same operations will be done at same time with a lot of differently sized small_sets and also will do shift operations over small_set, expanding it would lead in much more bits to CPU work on).
Example:
big_set: 100111001111100011000111110001100 (would be billions of bits in reality)
small_set: 111111, so ss_size is 6. (may be an unpredictable number of bits).
I need to take 6 bits length parts of big_set, e.g.: 001100, 000111, etc. Obs.: not necessarily Nth 6 bits, it could be from 3rd to 9th bits, for instance. I don't know how can I get it.
I don't want to get a big_set copy with everything zeroed except the 6 bits I would be taking, like on 000000001111100000000000000000000, as that would be also very RAM expensive.
The question is: how can I get N bits from anywhere inside big_set, so I can do bitwise operations between they and small_set? Being N = ss_size.
I'm not sure that the example given below will give an answer to your question, also I am not sure that the realized XOR will work correctly.
But I have tried to show how confusing can be the implementation of the algorithm, if the task is to save memory.
This is my example for case of 40 bit in big_set and 6 bit in small_set:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
void setBitsInMemory(uint8_t * memPtr, size_t from, size_t to)
// sets bits in the memory allocated from memPtr (pointer to the first byte)
// where from and to are numbers of bits to be set
{
for (size_t i = from; i <= to; i++)
{
size_t block = i / 8;
size_t offset = i % 8;
*(memPtr + block) |= 0x1 << offset;
}
}
uint8_t * allocAndBuildSmallSet(size_t bitNum)
// Allocate memory to store bitNum bits and set them to 1
{
uint8_t * ptr = NULL;
size_t byteNum = 1 + bitNum / 8; // determine number of bytes for
ptr = (uint8_t*) malloc(byteNum);
if (ptr != NULL)
{
for (size_t i = 0; i < byteNum; i++) ptr[i] = 0;
setBitsInMemory(ptr, 0, bitNum - 1);
}
return ptr;
}
void printBits(uint8_t * memPtr, size_t from, size_t to)
{
for (size_t i = from; i <= to; i++)
{
size_t block = i / 8;
size_t offset = i % 8;
if (*(memPtr + block) & (0x1 << offset) )
printf("1");
else
printf("0");
}
}
void applyXOR(uint8_t * mainMem, size_t start, size_t cnt, uint8_t * pattern, size_t ptrnSize)
// Applys bitwise XOR between cnt bits of mainMem and pattern
// starting from start bit in mainMem and 0 bit in pattern
// if pattern is smaller than cnt, it will be applyed cyclically
{
size_t ptrnBlk = 0;
size_t ptrnOff = 0;
for (size_t i = start; i < start + cnt; i++)
{
size_t block = i / 8;
size_t offset = i % 8;
*(mainMem + block) ^= ((*(pattern + ptrnBlk) & (0x1 << ptrnOff)) ? 1 : 0) << offset;
ptrnOff++;
if ((ptrnBlk * 8 + ptrnOff) >= ptrnSize)
{
ptrnBlk = 0;
ptrnOff = 0;
}
if (ptrnOff % 8 == 0)
{
ptrnBlk++;
ptrnOff = 0;
}
}
}
int main(void)
{
uint8_t * big_set;
size_t ss_size;
uint8_t * small_set;
big_set = (uint8_t*)malloc(5); // 5 bytes (40 bit) without initialization
ss_size = 6;
small_set = allocAndBuildSmallSet(ss_size);
printf("Initial big_set:\n");
printBits(big_set, 0, 39);
// some operation for ss_size bits starting from 12th
applyXOR(big_set, 12, ss_size, small_set, ss_size);
// output for visual analysis
printf("\nbig_set after XOR with small_set:\n");
printBits(big_set, 0, 39);
printf("\n");
// free memory
free(big_set);
free(small_set);
}
At my PC I can see the following:
This may be somewhat of an odd question as well as my first one ever on this site and a pretty complicated thing to ask basically I have this decompresser for a very specific archived file, I barely understand this but from what i can grasp its some sort of "bit mask" it reads the first 2 bytes out of target file, and stores them as a sequence.
The first for loop is where I get confused
Say for arguments sake mask is 2 bytes 10 04, or 1040(decimal) thats what it usually is in these files
for (t = 0; t<16; t++) {
if (mask & (1 << (15 - t))) {
This seems to be looping through all 16 bits of those 2 bytes and running an AND operation on mask (1040) on every bit?
The if statement is what I don't understand completely? Whats triggering the if? If the bit is greater then 0?
Because if mask is 1040, then really what were looking at is
if(1040 & 32768) index 15
if(1040 & 16384) index 14
if(1040 & 8192) index 13
if(1040 & 4096) index 12
if(1040 & 2048) index 11
if(1040 & 1024) index 10
if(1040 & 512) and so on.....
if(1040 & 256)
I just really need to know whats triggering this if statement? i think i might be over thinking it, but is it simply trigger if the current bit is greater then 0?
The only other thing i can do is compile this source myself, insert printfs on key variables and go hand in hand with a hex editor and try and figure out whats actually going on here, if anyone could give me a hand would be awesome.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
uint8_t dest[1024 * 1024 * 4]; // holds the actual data
int main(int argc, char *argv[]) {
FILE *fi, *fo;
char fname[255];
uint16_t mask, tmp, offset, length;
uint16_t seq;
uint32_t dptr, sptr;
uint16_t l, ct;
uint16_t t, s;
int test_len;
int t_length, t_off;
// Print Usage if filename is missing
if (argc<3) {
printf("sld_unpack - Decompressor for .sld files ()\nsld_unpack <filename.sld> <filename.t2>\n");
return(-1);
}
// Open .SLD-file
if (!(fi = fopen(argv[1], "rb"))) {
printf("Error opening %s\n", argv[1]);
return(-1);
}
dptr = 0;
fread((uint16_t*)&seq, 1, 2, fi); // read 1st 2 bytes in file
test_len = ftell(fi);
printf("[Main Header sequence: %d]\n 'offset' : %d \n", seq, test_len);
sptr = 0;
fread((uint16_t*)&seq, 1, 2, fi);
while (!feof(fi)) { // while not at the end of the file set mask equal to sequence (first 2 bytes of header)
mask = seq;
// loop through 16 bit mask
for (t = 0; t<16; t++) {
if (mask & (1 << (15 - t))) { // check all bit fields and run AND check to if value greater then 0?
test_len = ftell(fi);
fread((uint16_t*)&seq, 1, 2, fi); // read
sptr = sptr + 2; // set from 0 to 2
tmp = seq; // set tmp to sequence
offset = ((uint32_t)tmp & 0x07ff) * 2;
length = ((tmp >> 11) & 0x1f) * 2; // 32 - 1?
if (length>0) {
for (l = 0; l<length; l++) {
dest[dptr] = dest[dptr - offset];
dptr++;
}
}
else { // if length == 0
t_length = ftell(fi);
fread((uint16_t*)&seq, 1, 2, fi);
sptr = sptr + 2;
length = seq * 2;
for (s = 0; s<length; s++) {
dest[dptr] = dest[dptr - offset];
dptr++;
}
}
}
else { // if sequence AND returns 0 (or less)?
fread((uint16_t*)&seq, 1, 2, fi);
t_length = ftell(fi);
sptr = sptr + 2;
dest[dptr++] = seq & 0xff;
dest[dptr++] = (seq >> 8) & 0xff;
}
}
fread((uint16_t*)&seq, 1, 2, fi);
}
fclose(fi);
sprintf(fname, "%s\0", argv[2]);
if (!(fo = fopen(fname, "wb"))) { // if file
printf("Error creating %s\n", fname);
return(-1);
}
fwrite((uint8_t*)&dest, 1, dptr, fo);
fclose(fo);
printf("Done.\n");
return(0);
}
Be careful here.
for arguments sake mask is 2 bytes 10 04, or 1040(decimal)
That assumption may be nowhere close to true. You need to show how mask is defined, but generally a mask of bytes 10 (00001010) and 40 (00101000) is binary 101000101000 or decimal (2600) not quite 1040.
The general mask of 2600 decimal will match when bits 4,6,10 & 12 are set. Remember a bit mask is nothing more than a number whose binary representation when anded or ored with a second number produces some desired result. Nothing magic about a bit mask, its just a number with the right bits set for your intended purpose.
When you and two numbers together and test, your are testing whether there are common bits set in both numbers. Using the for loop and shift, you are doing a bitwise test for which common bits are set. Using the mask of 2600 with the loop counter will test true when bits 4,6,10 & 12 are set. In other words when the test clause equals 8, 32, 512 or 2048.
The following is a short example of what is happening in the loop and if statements.
#include <stdio.h>
/* BUILD_64 */
#if defined(__LP64__) || defined(_LP64)
# define BUILD_64 1
#endif
/* BITS_PER_LONG */
#ifdef BUILD_64
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif
/* CHAR_BIT */
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
char *binpad (unsigned long n, size_t sz);
int main (void) {
unsigned short t, mask;
mask = (10 << 8) | 40;
printf ("\n mask : %s (%hu)\n\n",
binpad (mask, sizeof mask * CHAR_BIT), mask);
for (t = 0; t<16; t++)
if (mask & (1 << (15 - t)))
printf (" t %2hu : %s (%hu)\n", t,
binpad (mask & (1 << (15 - t)), sizeof mask * CHAR_BIT),
mask & (1 << (15 - t)));
return 0;
}
/** returns pointer to binary representation of 'n' zero padded to 'sz'.
* returns pointer to string contianing binary representation of
* unsigned 64-bit (or less ) value zero padded to 'sz' digits.
*/
char *binpad (unsigned long n, size_t sz)
{
static char s[BITS_PER_LONG + 1] = {0};
char *p = s + BITS_PER_LONG;
register size_t i;
for (i = 0; i < sz; i++)
*--p = (n>>i & 1) ? '1' : '0';
return p;
}
Output
$ ./bin/bitmask1040
mask : 0000101000101000 (2600)
t 4 : 0000100000000000 (2048)
t 6 : 0000001000000000 (512)
t 10 : 0000000000100000 (32)
t 12 : 0000000000001000 (8)
The if statement is what I don't understand completely? Whats triggering the if? If the bit is greater then 0? ... I just really need to know whats triggering this if statement? i think i might be over thinking it, but is it simply trigger if the current bit is greater then 0?
The C (and C++) if statement "triggers" when the conditional statement evaluates to true, which is any non-zero value; zero equates to false.
Straight C doesn't have a Boolean type, it just use the convention of zero (0) is false, and any other value is true.
if (mask & (1 << (15 - t))) {...}
is the same as
if ((mask & (1 << (15 - t))) != 0) {...}
The expression you gave is only true (non-zero) when there is a bit in the mask in the same position that the 1 was shifted by. i.e. is the 15th bit in the mask set, etc.
N.b.
mask & (1 << (15 - t))
can only ever be 0 or 1 er... will only have one bit set.
Given a bytearray uint8_t data[N] what is an efficient method to find a byte uint8_t search within it even if search is not octet aligned? i.e. the first three bits of search could be in data[i] and the next 5 bits in data[i+1].
My current method involves creating a bool get_bit(const uint8_t* src, struct internal_state* state) function (struct internal_state contains a mask that is bitshifted right, &ed with src and returned, maintaining size_t src_index < size_t src_len) , leftshifting the returned bits into a uint8_t my_register and comparing it with search every time, and using state->src_index and state->src_mask to get the position of the matched byte.
Is there a better method for this?
If you're searching an eight bit pattern within a large array you can implement a sliding window over 16 bit values to check if the searched pattern is part of the two bytes forming that 16 bit value.
To be portable you have to take care of endianness issues which is done by my implementation by building the 16 bit value to search for the pattern manually. The high byte is always the currently iterated byte and the low byte is the following byte. If you do a simple conversion like value = *((unsigned short *)pData) you will run into trouble on x86 processors...
Once value, cmp and mask are setup cmp and mask are shifted. If the pattern was not found within hi high byte the loop continues by checking the next byte as start byte.
Here is my implementation including some debug printouts (the function returns the bit position or -1 if pattern was not found):
int findPattern(unsigned char *data, int size, unsigned char pattern)
{
int result = -1;
unsigned char *pData;
unsigned char *pEnd;
unsigned short value;
unsigned short mask;
unsigned short cmp;
int tmpResult;
if ((data != NULL) && (size > 0))
{
pData = data;
pEnd = data + size;
while ((pData < pEnd) && (result == -1))
{
printf("\n\npData = {%02x, %02x, ...};\n", pData[0], pData[1]);
if ((pData + 1) < pEnd) /* still at least two bytes to check? */
{
tmpResult = (int)(pData - data) * 8; /* calculate bit offset according to current byte */
/* avoid endianness troubles by "manually" building value! */
value = *pData << 8;
pData++;
value += *pData;
/* create a sliding window to check if search patter is within value */
cmp = pattern << 8;
mask = 0xFF00;
while (mask > 0x00FF) /* the low byte is checked within next iteration! */
{
printf("cmp = %04x, mask = %04x, tmpResult = %d\n", cmp, mask, tmpResult);
if ((value & mask) == cmp)
{
result = tmpResult;
break;
}
tmpResult++; /* count bits! */
mask >>= 1;
cmp >>= 1;
}
}
else
{
/* only one chance left if there is only one byte left to check! */
if (*pData == pattern)
{
result = (int)(pData - data) * 8;
}
pData++;
}
}
}
return (result);
}
I don't think you can do much better than this in C:
/*
* Searches for the 8-bit pattern represented by 'needle' in the bit array
* represented by 'haystack'.
*
* Returns the index *in bits* of the first appearance of 'needle', or
* -1 if 'needle' is not found.
*/
int search(uint8_t needle, int num_bytes, uint8_t haystack[num_bytes]) {
if (num_bytes > 0) {
uint16_t window = haystack[0];
if (window == needle) return 0;
for (int i = 1; i < num_bytes; i += 1) {
window = window << 8 + haystack[i];
/* Candidate for unrolling: */
for (int j = 7; j >= 0; j -= 1) {
if ((window >> j) & 0xff == needle) {
return 8 * i - j;
}
}
}
}
return -1;
}
The main idea is to handle the 87.5% of cases that cross the boundary between consecutive bytes by pairing bytes in a wider data type (uint16_t in this case). You could adjust it to use an even wider data type, but I'm not sure that would gain anything.
What you cannot safely or easily do is anything involving casting part or all of your array to a wider integer type via a pointer (i.e. (uint16_t *)&haystack[i]). You cannot be ensured of proper alignment for such a cast, nor of the byte order with which the result might be interpreted.
I don't know if it would be better, but i would use sliding window.
uint counter = 0, feeder = 8;
uint window = data[0];
while (search ^ (window & 0xff)){
window >>= 1;
feeder--;
if (feeder < 8){
counter++;
if (counter >= data.length) {
feeder = 0;
break;
}
window |= data[counter] << feeder;
feeder += 8;
}
}
//Returns index of first bit of first sequence occurrence or -1 if sequence is not found
return (feeder > 0) ? (counter+1)*8-feeder : -1;
Also with some alterations you can use this method to search for arbitrary length (1 to 64-array_element_size_in_bits) bits sequence.
If AVX2 is acceptable (with earlier versions it didn't work out so well, but you can still do something there), you can search in a lot of places at the same time. I couldn't test this on my machine (only compile) so the following is more to give to you an idea of how it could be approached than copy&paste code, so I'll try to explain it rather than just code-dump.
The main idea is to read an uint64_t, shift it right by all values that make sense (0 through 7), then for each of those 8 new uint64_t's, test whether the byte is in there. Small complication: for the uint64_t's shifted by more than 0, the highest position should not be counted since it has zeroes shifted into it that might not be in the actual data. Once this is done, the next uint64_t should be read at an offset of 7 from the current one, otherwise there is a boundary that is not checked across. That's fine though, unaligned loads aren't so bad anymore, especially if they're not wide.
So now for some (untested, and incomplete, see below) code,
__m256i needle = _mm256_set1_epi8(find);
size_t i;
for (i = 0; i < n - 6; i += 7) {
// unaligned load here, but that's OK
uint64_t d = *(uint64_t*)(data + i);
__m256i x = _mm256_set1_epi64x(d);
__m256i low = _mm256_srlv_epi64(x, _mm256_set_epi64x(3, 2, 1, 0));
__m256i high = _mm256_srlv_epi64(x, _mm256_set_epi64x(7, 6, 5, 4));
low = _mm256_cmpeq_epi8(low, needle);
high = _mm256_cmpeq_epi8(high, needle);
// in the qword right-shifted by 0, all positions are valid
// otherwise, the top position corresponds to an incomplete byte
uint32_t lowmask = 0x7f7f7fffu & _mm256_movemask_epi8(low);
uint32_t highmask = 0x7f7f7f7fu & _mm256_movemask_epi8(high);
uint64_t mask = lowmask | ((uint64_t)highmask << 32);
if (mask) {
int bitindex = __builtin_ffsl(mask);
// the bit-index and byte-index are swapped
return 8 * (i + (bitindex & 7)) + (bitindex >> 3);
}
}
The funny "bit-index and byte-index are swapped" thing is because searching within a qword is done byte by byte and the results of those comparisons end up in 8 adjacent bits, while the search for "shifted by 1" ends up in the next 8 bits and so on. So in the resulting masks, the index of the byte that contains the 1 is a bit-offset, but the bit-index within that byte is actually the byte-offset, for example 0x8000 would correspond to finding the byte at the 7th byte of the qword that was right-shifted by 1, so the actual index is 8*7+1.
There is also the issue of the "tail", the part of the data left over when all blocks of 7 bytes have been processed. It can be done much the same way, but now more positions contain bogus bytes. Now n - i bytes are left over, so the mask has to have n - i bits set in the lowest byte, and one fewer for all other bytes (for the same reason as earlier, the other positions have zeroes shifted in). Also, if there is exactly 1 byte "left", it isn't really left because it would have been tested already, but that doesn't really matter. I'll assume the data is sufficiently padded that accessing out of bounds doesn't matter. Here it is, untested:
if (i < n - 1) {
// make n-i-1 bits, then copy them to every byte
uint32_t validh = ((1u << (n - i - 1)) - 1) * 0x01010101;
// the lowest position has an extra valid bit, set lowest zero
uint32_t validl = (validh + 1) | validh;
uint64_t d = *(uint64_t*)(data + i);
__m256i x = _mm256_set1_epi64x(d);
__m256i low = _mm256_srlv_epi64(x, _mm256_set_epi64x(3, 2, 1, 0));
__m256i high = _mm256_srlv_epi64(x, _mm256_set_epi64x(7, 6, 5, 4));
low = _mm256_cmpeq_epi8(low, needle);
high = _mm256_cmpeq_epi8(high, needle);
uint32_t lowmask = validl & _mm256_movemask_epi8(low);
uint32_t highmask = validh & _mm256_movemask_epi8(high);
uint64_t mask = lowmask | ((uint64_t)highmask << 32);
if (mask) {
int bitindex = __builtin_ffsl(mask);
return 8 * (i + (bitindex & 7)) + (bitindex >> 3);
}
}
If you are searching a large amount of memory and can afford an expensive setup, another approach is to use a 64K lookup table. For each possible 16-bit value, the table stores a byte containing the bit shift offset at which the matching octet occurs (+1, so 0 can indicate no match). You can initialize it like this:
uint8_t* g_pLookupTable = malloc(65536);
void initLUT(uint8_t octet)
{
memset(g_pLookupTable, 0, 65536); // zero out
for(int i = 0; i < 65536; i++)
{
for(int j = 7; j >= 0; j--)
{
if(((i >> j) & 255) == octet)
{
g_pLookupTable[i] = j + 1;
break;
}
}
}
}
Note that the case where the value is shifted 8 bits is not included (the reason will be obvious in a minute).
Then you can scan through your array of bytes like this:
int findByteMatch(uint8_t* pArray, uint8_t octet, int length)
{
if(length >= 0)
{
uint16_t index = (uint16_t)pArray[0];
if(index == octet)
return 0;
for(int bit, i = 1; i < length; i++)
{
index = (index << 8) | pArray[i];
if(bit = g_pLookupTable[index])
return (i * 8) - (bit - 1);
}
}
return -1;
}
Further optimization:
Read 32 or however many bits at a time from pArray into a uint32_t and then shift and AND each to get byte one at a time, OR with index and test, before reading another 4.
Pack the LUT into 32K by storing a nybble for each index. This might help it squeeze into the cache on some systems.
It will depend on your memory architecture whether this is faster than an unrolled loop that doesn't use a lookup table.
I have a protocol that requires a length field up to 32-bits, and it must be
generated at runtime to describe how many bytes are in a given packet.
The code below is kind of ugly but I am wondering if this can be refactored to
be slightly more efficient or easily understandable. The problem is that the
code will only generate enough bytes to describe the length of the packet, so
less than 255 bytes = 1 byte of length, less than 65535 = 2 bytes of length
etc...
{
extern char byte_stream[];
int bytes = offset_in_packet;
int n = length_of_packet;
/* Under 4 billion, so this can be represented in 32 bits. */
int t;
/* 32-bit number used for temporary storage. */
/* These are the bytes we will break up n into. */
unsigned char first, second, third, fourth;
t = n & 0xFF000000;
/* We have used AND to "mask out" the first byte of the number. */
/* The only bits which can be on in t are the first 8 bits. */
first = t >> 24;
if (t) {
printf("byte 1: 0x%02x\n",first );
byte_stream[bytes] = first; bytes++;
write_zeros = 1;
}
/* Now we shift t so that it is between 0 and 255. This is the first, highest byte of n. */
t = n & 0x00FF0000;
second = t >> 16;
if (t || write_zeros) {
printf("byte 2: 0x%02x\n", second );
byte_stream[bytes] = second; bytes++;
write_zeros = 1;
}
t = n & 0x0000FF00;
third = t >> 8;
if ( t || write_zeros) {
printf("byte 3: 0x%02x\n", third );
byte_stream[bytes] = third; bytes++;
write_zeros = 1;
}
t = n & 0x000000FF;
fourth = t;
if (t || write_zeros) {
printf("byte 4: 0x%02x\n", fourth);
byte_stream[bytes] = fourth; bytes++;
}
}
You should really use a fixed-width field for your length.
When the program on the receiving end has to read the length field of your packet, how does it know where the length stops?
If the length of a packet can potentially reach 4 GB, does a 1-3 byte overhead really matter?
Do you see how complex your code has already become?
Really you're only doing four calculations, so readability seems way more important here than efficiency. My approach to make something like this more readable is to
Extract common code to a function
Put similar calculations together to make the patterns more obvious
Get rid of the intermediate variable print_zeroes and be explicit about the cases in which you output bytes even if they're zero (i.e. the preceding byte was non-zero)
I've changed the random code block into a function and changed a few variables (underscores are giving me trouble in the markdown preview screen). I've also assumed that bytes is being passed in, and that whoever is passing it in will pass us a pointer so we can modify it.
Here's the code:
/* append byte b to stream, increment index */
/* really needs to check length of stream before appending */
void output( int i, unsigned char b, char stream[], int *index )
{
printf("byte %d: 0x%02x\n", i, b);
stream[(*index)++] = b;
}
void answer( char bytestream[], unsigned int *bytes, unsigned int n)
{
/* mask out four bytes from word n */
first = (n & 0xFF000000) >> 24;
second = (n & 0x00FF0000) >> 16;
third = (n & 0x0000FF00) >> 8;
fourth = (n & 0x000000FF) >> 0;
/* conditionally output each byte starting with the */
/* first non-zero byte */
if (first)
output( 1, first, bytestream, bytes);
if (first || second)
output( 2, second, bytestream, bytes);
if (first || second || third)
output( 3, third, bytestream, bytes);
if (first || second || third || fourth)
output( 4, fourth, bytestream, bytes);
}
Ever so slightly more efficient, and maybe easier to understand would be this modification to the last four if statements:
if (n>0x00FFFFFF)
output( 1, first, bytestream, bytes);
if (n>0x0000FFFF)
output( 2, second, bytestream, bytes);
if (n>0x000000FF)
output( 3, third, bytestream, bytes);
if (1)
output( 4, fourth, bytestream, bytes);
I agree, however, that compressing this field makes the receiving state machine overly complicated. But if you can't change the protocol, this code is much easier to read.
Try this loop:
{
extern char byte_stream[];
int bytes = offset_in_packet;
int n = length_of_packet; /* Under 4 billion, so this can be represented in 32 bits. */
int t; /* 32-bit number used for temporary storage. */
int i;
unsigned char curByte;
for (i = 0; i < 4; i++) {
t = n & (0xFF000000 >> (i * 16));
curByte = t >> (24 - (i * 8));
if (t || write_zeros) {
printf("byte %d: 0x%02x\n", i, curByte );
byte_stream[bytes] = curByte;
bytes++;
write_zeros = 1;
}
}
}
I'm not sure I understand your question. What exactly are you trying to count? If I understand correctly you're trying to find the Most Significant non-zero byte.
You're probably better off using a loop like this:
int i;
int write_zeros = 0;
for (i = 3; i >=0 ; --i) {
t = (n >> (8 * i)) & 0xff;
if (t || write_zeros) {
write_zeros = 1;
printf ("byte %d : 0x%02x\n", 4-i, t);
byte_stream[bytes++] = t;
}
}