1 回答

TA貢獻1793條經驗 獲得超6個贊
到目前為止,我已經 [...]
byte[] data = new byte[bytesCount];
不幸的是,這種方法只有在您的位偏移量是 8 的倍數時才有效。在所有其他情況下,您必須劃分要復制的每個字節。下圖說明了如何劃分每個字節以及將劃分的部分放在哪里。
MSB = 最高有效位
LSB = 最低有效位
由于有很多極端情況,實現上述算法有點棘手。以下實現通過了您的所有測試和我的所有測試。我使用了許多變量來為所有計算賦予有意義的名稱,希望它更容易理解。您可以通過消除其中一些變量并就地計算一些值來縮短實施時間。
我冒昧地將您的功能重命名toBytes為bitSubstring. 對于已經將字節作為輸入的方法,以前的名稱toBytes似乎有點不合時宜。
public static byte[] bitSubstring(int byteOffset, int bitOffset,
int lengthInBits, byte... source) {
return bitSubstring(8 * byteOffset + bitOffset, lengthInBits, source);
}
public static byte[] bitSubstring(int startBit, int lengthInBits,
byte... source) {
assert startBit >= 0 && startBit < 8 * source.length;
assert lengthInBits >= 0 && startBit + lengthInBits <= 8 * source.length;
int lengthInBytes = (int) Math.ceil(lengthInBits / 8.0);
byte[] target = new byte[lengthInBytes];
int startByte = startBit / 8;
int endBitExclusive = startBit + lengthInBits;
int endByteExclusive = (int) Math.ceil(endBitExclusive / 8.0);
int sourceBytesToRead = endByteExclusive - startByte;
int lowerPartSize = 8 * endByteExclusive - endBitExclusive;
int shiftLowerUp = (8 - lowerPartSize);
int shiftUpperDown = lowerPartSize;
int lastSrc = 0;
if (sourceBytesToRead > lengthInBytes) {
lastSrc = source[startByte] & 0xFF;
startByte++;
}
for (int targetByte = 0; targetByte < target.length; ++targetByte) {
int curSrc = source[startByte + targetByte] & 0xFF;
target[targetByte] |= (lastSrc << shiftLowerUp)
| (curSrc >>> shiftUpperDown);
lastSrc = curSrc;
}
int overhang = 8 * lengthInBytes - lengthInBits;
if (overhang > 0) {
target[0] &= 0xFF >>> overhang;
}
return target;
}
上面的算法應該相當快。但是,如果您只對實現大小和可讀性感興趣,那么逐位復制的方法會更好。
public static byte[] bitSubstringSlow(int startBitSource, int lengthInBits,
byte... source) {
byte[] target = new byte[(int) Math.ceil(lengthInBits / 8.0)];
int startBitTarget = (8 - lengthInBits % 8) % 8;
for (int i = 0; i < lengthInBits; ++i) {
setBit(target, startBitTarget + i, getBit(source, startBitSource + i));
}
return target;
}
public static int getBit(byte[] source, int bitIdx) {
return (source[bitIdx / 8] >>> (7 - bitIdx % 8)) & 1;
}
public static void setBit(byte[] target, int bitIdx, int bitValue) {
int block = bitIdx / 8;
int shift = 7 - bitIdx % 8;
target[block] &= ~(1 << shift);
target[block] |= bitValue << shift;
}
......或更少可重復使用但更短:
public static byte[] bitSubstringSlow2(int startBitSource, int lengthInBits,
byte... source) {
byte[] target = new byte[(int) Math.ceil(lengthInBits / 8.0)];
int startBitTarget = (8 - lengthInBits % 8) % 8;
for (int i = 0; i < lengthInBits; ++i) {
int srcIdx = startBitSource + i;
int tgtIdx = startBitTarget + i;
target[tgtIdx / 8] |= ((source[srcIdx / 8] >>> (7 - srcIdx % 8)) & 1)
<< (7 - tgtIdx % 8);
}
return target;
}
添加回答
舉報