Python 3.1.3 ctypes.structure не упорядочивает биты должным образом и неожиданно изменяет данные - PullRequest
2 голосов
/ 03 февраля 2011

Я определил следующую структуру

import ctypes
from ctypes import *
class r( BigEndianStructure ):
    _fields_ = [( "d0", c_uint32, 28 ),
                ( "d1", c_uint32, 18 ),
                ( "d2", c_uint32, 18 ),
                ( "d3", c_uint16, 16 ),
                ( "d4", c_uint16, 16 ), ]

затем проверяется со следующим кодом

a = r(0xAAAAAAAA,0xBBBBBBBB,0xCCCCCCCC,0xDDDD,0xEEEE)
for byte in string_at( addressof( a ), sizeof( a ) ):
    print(hex(byte),end="")

результат

0xaa 0xaa 0xaa 0xa0 0xee 0xee 0xc0 0x0 0x33 0x33 0x0 0x0 0xdd 0xdd 0xee 0xee

ожидаемый результат был

0xaa 0xaa 0xaa 0xa0 0xbb 0xbb 0xc0 0x0 0xcc 0xcc 0xc0 0x0 0xdd 0xdd 0xee 0xee

Мало того, что структура не была уплотнена, данные результата отличаются от того, что было введено. я сделал какую-то ошибку, или Python любит изменять данные по своему усмотрению?

Ответы [ 3 ]

0 голосов
/ 04 февраля 2011

Похоже, проблема заключается в сохранении 18-битных значений ширины в 32-битных, а затем интерпретировать их как полные 32-битные значения.

Давайте посмотрим, что происходит с 0xBBBBBBBB:

0xBBBBBBBB = 10111011101110111011101110111011b
0xBBBBBBBB & 3FFFF (bit width of 18) = 111011101110111011b

Если ваши структуры выглядят как | d0 | 4 pad bits | d1 | 14 pad bits | d2 | 14 pad bits ... |, то интерпретация адреса d1 как 32-битного значения будет:

11101110111011101100000000000000b = 0xEEEEC000

В основном, когда вы читаете память таким образом, вместо получения 18-битной маски ожидаемого значения вы получаете 14-битный сдвиг.

0 голосов
/ 04 февраля 2011

Используйте битовые поля, которые соответствуют типу контейнера, чтобы избежать выравнивания выравнивания. В приведенном ниже примере 4 + 8 + 16 соответствует c_uint32, а 4 + 8 + 16 + 5 - нет, поэтому d3 выравнивается в следующем c_uint32:

from ctypes import *
class r( BigEndianStructure ):
    _fields_ = [('d0',c_uint32, 4),
                ('d1',c_uint32, 8),
                ('d2',c_uint32,16),
                ('d3',c_uint32, 5)]

def fld(n):
    return '[' + '-'*(n-2) + ']'

def pad(n):
    return '.'*n

print(fld(4),fld(8),fld(16),pad(4),fld(5),pad(27),sep='')

for i in range(1,17):
    v = 2**i-1
    a = r(v,v,v,v)
    for byte in string_at( addressof( a ), sizeof( a ) ):
        print('{0:08b}'.format(byte),end='',sep='')
    print()

выход

Двоичный вывод облегчает визуализацию чисел. Обратите внимание, что 5-битное поле не может поместиться в оставшиеся 4 бита первого c_uint32, поэтому были добавлены 4 бита заполнения для запуска 5-битного поля в следующем c_uint32.

[--][------][--------------]....[---]...........................
0001000000010000000000000001000000001000000000000000000000000000
0011000000110000000000000011000000011000000000000000000000000000
0111000001110000000000000111000000111000000000000000000000000000
1111000011110000000000001111000001111000000000000000000000000000
1111000111110000000000011111000011111000000000000000000000000000
1111001111110000000000111111000011111000000000000000000000000000
1111011111110000000001111111000011111000000000000000000000000000
1111111111110000000011111111000011111000000000000000000000000000
1111111111110000000111111111000011111000000000000000000000000000
1111111111110000001111111111000011111000000000000000000000000000
1111111111110000011111111111000011111000000000000000000000000000
1111111111110000111111111111000011111000000000000000000000000000
1111111111110001111111111111000011111000000000000000000000000000
1111111111110011111111111111000011111000000000000000000000000000
1111111111110111111111111111000011111000000000000000000000000000
1111111111111111111111111111000011111000000000000000000000000000
0 голосов
/ 04 февраля 2011

Поля выровнены по левому краю (дополнены вправо)

Поле d0 -

(AAAA AAAA & FFFFFFF (28 bits)) << (32 - 28 = 4) = AAAA AAA0

Поле d1 -

(BBBB BBBB & 3FFFF (18 bits)) << (32 - 18 = 14)  = EEEE C000

Поле d2 -

(CCCC CCCC & 3FFFF (18 bits)) << (32 - 18 = 14) = 3333 0000

Поля d1 и d2 не будут вписываться в 32-битное поле, поэтому d2 выравнивается в следующем 32-битном интервале.

Иллюстративный пошаговый пример для поля d1:

BBBB BBBB & 3FFFF (only least-significant 18 bits kept) = 3BBBB

3BBBB << 14 (pad the last 14 bits) = EEEE C000
...