Go: битовые поля и битовая упаковка

Битовые поля языка C предоставляют довольно удобный метод определения полей произвольной ширины в структуре (не говоря уже о проблемах с переносимостью). Например, вот простая структура с парой полей и «флагом»:

#pragma pack(push,1)
struct my_chunk{

    unsigned short fieldA: 16;
    unsigned short fieldB: 15;
    unsigned short fieldC:  1;
};
#pragma pop()

Добавление операторов #pragma упаковывает эту структуру в 32-битное слово (обеспечивая, например, выравнивание манипуляций с my_chunk указателями, а также экономию места).

Доступ к каждому полю синтаксически очень удобен:

struct my_chunk aChunk;
aChunk.fieldA = 3;
aChunk.fieldB = 2;
aChunk.fieldC = 1;

Альтернативный способ сделать это без помощи языка довольно уродлив и в значительной степени превращается в ассемблер. например одно из решений состоит в том, чтобы иметь макросы битового сдвига для каждого поля, к которому вы хотите получить доступ:

#define FIELD_A  0xFF00
#define FIELD_B  0x00FE
#define FIELD_C  0x0001

#define get_field(p, f) ((*p)&f)
#define set_field(p, f, v) (*p) = (v<<f) + (*p)&(~f)

...
set_field(&my_chunk, FIELD_A, 12345);

.. или примерно так (для большей формальности взгляните на это)

Итак, вопрос в том, если я хочу «делать» битовые поля в go, как лучше всего это сделать?


person Rooke    schedule 26.04.2011    source источник


Ответы (2)


"В настоящее время в Go нет планов по битовым полям структуры."

Для этого вы можете написать пакет Go; ассемблер не требуется.

person peterSO    schedule 26.04.2011
comment
Обратите внимание, что на самом деле он имел в виду не использование ассемблера, а то, что он выглядит как ассемблер со всеми необходимыми маскированием/сдвигом, когда вам нужно получить доступ к таким подполям вручную. - person Alexis Wilke; 27.08.2019

Если цель состоит в том, чтобы иметь очень маленькую структуру, вы, вероятно, просто сделаете:

package main

import "fmt"

type my_chunk uint32

func (c my_chunk) A() uint16 {
  return uint16((c & 0xffff0000) >> 16) 
}

func (c *my_chunk) SetA(a uint16) {
  v := uint32(*c)
  *c = my_chunk((v & 0xffff) | (uint32(a) << 16))
}

func main() {
  x := my_chunk(123)
  x.SetA(12)
  fmt.Println(x.A())
}

С текущими 6g/8g вы смотрите на вызов функции с ~ 6 инструкциями для геттера, и со временем такие вызовы, вероятно, будут встроены.

person Kyle C    schedule 03.05.2011
comment
Просто дополнение: я пытался сделать это со структурой (в отличие от одной переменной «chunk») с uint16 и uint32, и, к сожалению, go дает Sizeof() = 8, а не 6 для структуры. В остальном большое (с опозданием) спасибо за ответ, очень помог - person Rooke; 27.06.2011
comment
Да, со структурой члены, скорее всего, будут выровнены по словам. Если вы уже используете unsafe, вы можете использовать [3]uint16 и получить часть uint32 с помощью (*uint32)(unsafe.Pointer(&val[1])), что позволяет избежать затрат на упаковку/распаковку, но это немного некрасиво. - person Kyle C; 29.06.2011