/*
This code is hereby released to the public domain.
~aaaaaa123456789, 2016-01-02
*/
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define multibyte_is_zero(operand, size) ((int) ( \
((int) (size)) && \
!(*((const unsigned char *) (operand))) && \
!memcmp(operand, ((const unsigned char *) (operand)) + 1, ((int) (size)) - 1) \
))
#define multibyte_is_negative(operand, size) (((int) (((const unsigned char *) (operand))[((int) (size)) - 1])) & 0x80)
void multibyte_op (unsigned char * result, const unsigned char * operand0, const unsigned char * operand1, int op, unsigned size) {
/*
every two bits of op determine what to do with some intermediate result: 00 = nothing, 01 = subtract, 10 = add, 11 = add w/ negated shift
bits 31-30: (operand0 AND operand1) >> shift amount, bits 29-28: (operand0 OR operand1) >> shift amount,
bits 27-26: (operand0 AND operand1) << shift amount, bits 25-24: (operand0 OR operand1) << shift amount
bits 23-0 of op indicate shift amount
operand0 defaults to 0 if null, operand1 defaults to -1 if null
EXAMPLE VALUES OF OP:
0x80000000 = AND, 0x20000000 = OR, 0x60000000 = XOR, 0xa0000000 = addition
0x60000000 with operand1 set to NULL = NOT, 0x10000000 with operand0 set to NULL = negate (-x)
0x2300xxxx = rotate right by xxxx, 0x3200xxxx = rotate left by xxxx, 0x2000xxxx = shift right by xxxx, 0x0200xxxx = shift left by xxxx
(set operand0 to NULL for rotations and shifts)
*/
if (!(size && result)) return;
if (!(op & 0xff000000)) {
memset(result, 0, size);
return;
}
char * op0 = NULL;
char * op1 = NULL;
if (!operand0) operand0 = op0 = calloc(size, 1);
if (!operand1) operand1 = op1 = memset(malloc(size), -1, size);
unsigned char * temp_and = NULL;
unsigned char * temp_or = NULL;
if (op & 0xcc000000) temp_and = malloc(size);
if (op & 0x33000000) temp_or = malloc(size);
const unsigned char * p0 = operand0;
const unsigned char * p1 = operand1;
unsigned char * p2 = temp_and;
unsigned char * p3 = temp_or;
unsigned rem;
for (rem = size; rem >= sizeof(uint_fast8_t); rem -= sizeof(uint_fast8_t)) {
if (temp_and) {
*((uint_fast8_t *) p2) = *((const uint_fast8_t *) p0) & *((const uint_fast8_t *) p1);
p2 += sizeof(uint_fast8_t);
}
if (temp_or) {
*((uint_fast8_t *) p3) = *((const uint_fast8_t *) p0) | *((const uint_fast8_t *) p1);
p3 += sizeof(uint_fast8_t);
}
p0 += sizeof(uint_fast8_t);
p1 += sizeof(uint_fast8_t);
}
for (; rem; rem --) {
if (temp_and) *(p2 ++) = *p0 & *p1;
if (temp_or) *(p3 ++) = *p0 | *p1;
p0 ++;
p1 ++;
}
free(op0);
free(op1);
int shift_amount = op & 0xffffff;
if (shift_amount & 0x800000) shift_amount -= 0x1000000;
shift_amount %= size << 3;
if (shift_amount < 0) shift_amount += size << 3;
unsigned char ** partials = calloc(sizeof(unsigned char *), 4);
int inverted_shift_amount = (size << 3) - shift_amount;
unsigned pending_shift_list = 0;
int temp, pos;
if (shift_amount) {
if (op & 0xc0000000) {
*partials = calloc(size, 1);
temp = ((op & 0xc0000000) == 0xc0000000) ? inverted_shift_amount : shift_amount;
memcpy(*partials, temp_and + (temp >> 3), size - (temp >> 3));
pending_shift_list |= temp & 7;
}
if (op & 0x30000000) {
partials[1] = calloc(size, 1);
temp = ((op & 0x30000000) == 0x30000000) ? inverted_shift_amount : shift_amount;
memcpy(partials[1], temp_or + (temp >> 3), size - (temp >> 3));
pending_shift_list |= (temp & 7) << 3;
}
if (op & 0xc000000) {
partials[2] = calloc(size, 1);
temp = ((op & 0xc000000) == 0xc000000) ? inverted_shift_amount : shift_amount;
memcpy(partials[2] + (temp >> 3), temp_and, size - (temp >> 3));
pending_shift_list |= (temp & 7) << 6;
}
if (op & 0x3000000) {
partials[3] = calloc(size, 1);
temp = ((op & 0x3000000) == 0x3000000) ? inverted_shift_amount : shift_amount;
memcpy(partials[3] + (temp >> 3), temp_or, size - (temp >> 3));
pending_shift_list |= (temp & 7) << 9;
}
unsigned current_shift;
for (current_shift = 0; current_shift < 4; pending_shift_list >>= 3, current_shift ++) {
shift_amount = pending_shift_list & 7;
if (!shift_amount) continue;
temp = 0;
if (current_shift & 2)
for (pos = 0; pos < size; pos ++) {
temp |= partials[current_shift][pos] << shift_amount;
partials[current_shift][pos] = temp & 0xff;
temp >>= 8;
}
else
for (pos = size - 1; pos >= 0; pos --) {
temp = (temp << 8) | partials[current_shift][pos];
partials[current_shift][pos] = (temp >> shift_amount) & 0xff;
}
}
} else {
if (op & 0xc0000000) *partials = memcpy(malloc(size), temp_and, size);
if (op & 0x30000000) partials[1] = memcpy(malloc(size), temp_or, size);
if (op & 0x0c000000) partials[2] = memcpy(malloc(size), temp_and, size);
if (op & 0x03000000) partials[3] = memcpy(malloc(size), temp_or, size);
}
free(temp_and);
free(temp_or);
temp = 0;
int cp;
for (pos = 0; pos < size; pos ++) {
for (cp = 0; cp < 4; cp ++)
if (op & (1 << (31 - (cp << 1))))
temp += (int) partials[cp][pos];
else if (op & (1 << (30 - (cp << 1))))
temp -= (int) partials[cp][pos];
result[pos] = temp & 0xff;
temp >>= 8;
}
for (cp = 0; cp < 4; cp ++) free(partials[cp]);
free(partials);
}
Comments