bfs: fix misaligned access
The BFS on-disk data is not aligned. Reading it to memory and trying to access fields directly does not work on sparc. memcpy the data to an aligned variable before handling it with its native size. gcc knows how to access unaligned data, but we need to tell it when to do so. This is done with the "packed" attribute, but it works only on structs. So we have to wrap the values in a struct. Thanks to C++ features, we can make the struct relatively transparent by having an assigment operator (for writes) and a cast operator (for read access), so there is no need to access the value inside the struct with ".value" everywhere. The rest of the code is then largely unchanged (except for use in printf statements and other vararg functions, where the implicit casting can't work). gcc takes care of performing the access in the correct way on platforms that need it (old ARM, sparc) and can still optimize things on other architectures where specific code is not needed for unaligned access. Fixes #9255 Change-Id: I3bf62590dee059ad32b1845bdc4eace165b73203 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2363 Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
b81d67ed0c
commit
60f8e54f2b
@@ -1055,7 +1055,7 @@ BPlusTree::_FindKey(const bplustree_node* node, const uint8* key,
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
off_t* values = node->Values();
|
||||
Unaligned<off_t>* values = node->Values();
|
||||
int16 saveIndex = -1;
|
||||
|
||||
// binary search in the key array
|
||||
@@ -2934,7 +2934,7 @@ bplustree_node::KeyAt(int32 index, uint16* keyLength) const
|
||||
return NULL;
|
||||
|
||||
uint8* keyStart = Keys();
|
||||
uint16* keyLengths = KeyLengths();
|
||||
Unaligned<uint16>* keyLengths = KeyLengths();
|
||||
|
||||
*keyLength = BFS_ENDIAN_TO_HOST_INT16(keyLengths[index])
|
||||
- (index != 0 ? BFS_ENDIAN_TO_HOST_INT16(keyLengths[index - 1]) : 0);
|
||||
@@ -3008,7 +3008,7 @@ bplustree_node::CheckIntegrity(uint32 nodeSize) const
|
||||
}
|
||||
if (Values()[i] == -1) {
|
||||
dprintf("invalid node %p, value %d: %" B_PRIdOFF ": values "
|
||||
"corrupted\n", this, (int)i, Values()[i]);
|
||||
"corrupted\n", this, (int)i, Values()[i].value);
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,18 @@ enum bplustree_types {
|
||||
struct duplicate_array;
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct __attribute__((packed)) Unaligned {
|
||||
T value;
|
||||
|
||||
Unaligned<T>& operator=(const T& newValue)
|
||||
{
|
||||
value = newValue; return *this;
|
||||
}
|
||||
operator T() const { return value; }
|
||||
};
|
||||
|
||||
|
||||
struct bplustree_node {
|
||||
int64 left_link;
|
||||
int64 right_link;
|
||||
@@ -97,8 +109,8 @@ struct bplustree_node {
|
||||
{ return BFS_ENDIAN_TO_HOST_INT16(
|
||||
all_key_length); }
|
||||
|
||||
inline uint16* KeyLengths() const;
|
||||
inline off_t* Values() const;
|
||||
inline Unaligned<uint16>* KeyLengths() const;
|
||||
inline Unaligned<off_t>* Values() const;
|
||||
inline uint8* Keys() const;
|
||||
inline int32 Used() const;
|
||||
uint8* KeyAt(int32 index, uint16* keyLength) const;
|
||||
@@ -559,18 +571,19 @@ bplustree_header::IsValidLink(off_t link) const
|
||||
// #pragma mark - bplustree_node inline functions
|
||||
|
||||
|
||||
inline uint16*
|
||||
inline Unaligned<uint16>*
|
||||
bplustree_node::KeyLengths() const
|
||||
{
|
||||
return (uint16*)(((char*)this) + key_align(sizeof(bplustree_node)
|
||||
return (Unaligned<uint16>*)(((char*)this) + key_align(sizeof(bplustree_node)
|
||||
+ AllKeyLength()));
|
||||
}
|
||||
|
||||
|
||||
inline off_t*
|
||||
inline Unaligned<off_t>*
|
||||
bplustree_node::Values() const
|
||||
{
|
||||
return (off_t*)((char*)KeyLengths() + NumKeys() * sizeof(uint16));
|
||||
return (Unaligned<off_t>*)(
|
||||
(char*)KeyLengths() + NumKeys() * sizeof(uint16));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user