」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > Go 中的位元遮罩:選項管理的強大技術

Go 中的位元遮罩:選項管理的強大技術

發佈於2024-08-02
瀏覽:903

Introdução

Bitmasking é uma técnica eficiente e poderosa utilizada na programação para representar e manipular conjuntos de opções usando operações bitwise. Esta técnica permite armazenar múltiplos estados booleanos em um único valor numérico, onde cada bit representa uma opção distinta. Embora eu tenha começado minha jornada de programação com PHP, onde bitmasking é bastante utilizado, descobri que essa técnica é igualmente poderosa em outras linguagens como C, Java e até mesmo nas linguagens mais modernas como Go.

Neste artigo, vou compartilhar como implementar bitmasking em Go e discutir alguns exemplos práticos baseados na minha experiência.

Conceitos Básicos

O que é Bitmasking?

Bitmasking envolve o uso de operações bitwise para gerenciar conjuntos de flags ou opções. Cada opção é representada por um bit em um valor inteiro, permitindo que múltiplas opções sejam combinadas e verificadas de maneira eficiente através da compactação de dados, permitindo economizar espaço de memória e melhorar o desmpenho de programas críticos.

Operadores Bitwise

Os operadores bitwise mais comuns utilizados em bitmasking são:

  • AND (&): Usado para verificar se um bit específico está definido.
  • OR (|): Usado para definir bits específicos.
  • XOR (^): Usado para alternar bits específicos.
  • NOT (~): Usado para inverter todos os bits.

Implementação em Go

Vamos criar uma implementação de bitmasking em Go, utilizando um exemplo de sistema de configuração para uma estrutura chamada Service.

Usaremos o tipo iota para definir constantes de opções, onde cada constante representa uma opção específica como um bit único.

package main

import (
    "fmt"
)

type ServiceOption int

const (
    WITH_OPTION_A ServiceOption = 1 



Mas atenção, com o tipo intpodemos definir somente no máximo 32 opções de flag. Por isso, Ao definir uma flag esteja atento à possibilidade de crescimento desse conjunto.

Se você precisa superar a limitação de 32 flags que um tipo int permite, você pode considerar algumas alternativas que suportam mais bits. Aqui estão algumas opções:

Inteiros de 64 Bits

Em Go, você pode usar o tipo int64 para representar até 64 flags.

type ServiceOption int64
Usar um Array de Inteiros

Se você precisar de um número ainda maior de flags, pode usar um array ou slice de inteiros. Cada elemento do array pode armazenar 32 ou 64 flags, dependendo do tipo de inteiro usado (int32 ou int64).

type ServiceOption int64
type ServiceOptions [2]int64 // 2 * 64 = 128 flags

const (
    WITH_OPTION_A ServiceOption = 1 



Você também pode criar um tipo personalizado que usa slices ou arrays internamente para armazenar bits, mas torna tudo um pouco mais complexo, então adicionei um exemplo implementação no Go Playground

Atribuindo as flags na estrutura de dados

Ao Definirmos nossa bitmask agora vamos anexá-lo à uma estrutura chamada Service que incluirá um campo flags para armazenar as opções combinadas, vamos usar o operador Bitwise| OR para para definir bits específicos na criação do objeto.

type Service struct {
    flags ServiceOption
}

func NewService(flags ...ServiceOption) *Service {
    var opts ServiceOption
    for _, flag := range flags {
        opts |= flag
    }
    return &Service{
        flags: opts,
    }
}
Verificando se uma flag existe no bitmask

Com o construtor completo agora só precisamos criar uma forma de verificar se uma determinada opção está definida , vamos implementar o método HasOption com o operador bitwise &AND para retornar a existência da flag dentro da nossa bitmask de flags.

func (s *Service) HasOption(flag ServiceOption) bool {
    return s.flags&flag != 0
}

func main() {
    defaultService := NewService()
    fmt.Println("Default Service")
    fmt.Println("Has Option A:", defaultService.HasOption(WITH_OPTION_A))
    fmt.Println("Has Option B:", defaultService.HasOption(WITH_OPTION_B))

    modifiedService := NewService(WITH_OPTION_A | WITH_OPTION_B)
    fmt.Println("\nModified Service")
    fmt.Println("Has Option A:", modifiedService.HasOption(WITH_OPTION_A))
    fmt.Println("Has Option B:", modifiedService.HasOption(WITH_OPTION_B))
}

Agora nosso exemplo está completo, https://go.dev/play/p/rcHwLs-rUaA

Image description
Exemplo de uso do Iota para definir constantes Enums que representam dias da semana fonte

Exemplos de Uso no mundo real

No exemplo acima nós criamos duas instâncias de um serviço sem muita função, apenas para demostrar como podemos aplicar diferentes flags e com as opções sendo modificadas de acordo com os valores definidos no seu construtor, eliminando a necessidade de diversas flags boleanas e tornando o conjunto de modificadores expansíveis.

Um exemplo clássico de uso de bitmasking é em sistemas de permissões, onde diferentes níveis de acesso (leitura, escrita, execução) são representados por diferentes bits.

type Permission int

const (
    Read Permission = 1 



Neste exemplo, podemos ver como é simples e eficiente verificar múltiplas permissões combinando-as em um único valor inteiro.
Vamos supor que eu queira incluir novas permissõs como Delete e Share,

Basta que eu defina novas permissões à minhas constantes:

const (
    Read Permission = 1 



Essas permissões ainda podem ser armazenadas em um banco de dados por exemplo

Vamos assumir que temos uma tabela chamada users com um campo permissions que armazena o valor das permissões usando bitmask.

CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT,
    permissions INTEGER
);

Como o bitmask é um inteiro, ele será armazenado no banco de dados de forma bem direta, sem muitas complicações, reduzindo tamanhos de tabelas e dados armazenados.

Um Porém cuidado, caso uma permissão seja renomeada ou movida de posição na constante irá mudar o valor inteiro, tornando initulizável o valor armazenado.

No exemplo acima a permissão Read | Write irá imprimir o valor inteiro 3. Porém vamos supor que você queira melhorar a legibilidade do seu código adicionando a primeira declaração do iota como um valor vazio, sumindo um usuário sem permissão alguma.

const (
    _ Permission = 1 



A permissão Read | Write agorá irá imprimir o valor 10 ao invés de 3.

Exemplo permissões de sistema

Configurações de inicialização ou opções de sistema podem ser combinadas e verificadas usando bitmasking para determinar o comportamento do sistema.

type SystemOption int

const (
    EnableLogging SystemOption = 1 



Um exemplo um pouco mais avançado...

O uso de bitwise e bitmasking pode ser encontrado em operações de gráficos computacionais, onde frequentemente manipulamos pixels e cores.

Em gráficos computacionais, as cores são frequentemente representadas por valores RGBA (Red, Green, Blue, Alpha), onde cada componente da cor é armazenado em um byte (8 bits). Podemos usar operações bitwise para manipular essas cores.

O exemplo abaixo mostra como um programa que inverte as cores de uma imagem usando operações bitwise.

package main

import (
    "image"
    "image/color"
    "image/draw"
    "image/jpeg"
    "image/png"
    "log"
    "os"
)

// Inverte a cor de um pixel usando operações bitwise
func invertColor(c color.Color) color.Color {
    r, g, b, a := c.RGBA()
    return color.RGBA{
        R: uint8(^r >> 8),
        G: uint8(^g >> 8),
        B: uint8(^b >> 8),
        A: uint8(a >> 8), // Alpha não é invertido
    }
}

// Função para inverter as cores de uma imagem
func invertImageColors(img image.Image) image.Image {
    bounds := img.Bounds()
    invertedImg := image.NewRGBA(bounds)
    draw.Draw(invertedImg, bounds, img, bounds.Min, draw.Src)

    for y := bounds.Min.Y; y 



Nesse código a invertColor recebe uma cor (color.Color) e inverte seus componentes RGB usando a operação bitwise NOT (^). O componente Alpha (A) não é invertido.
c.RGBA() retorna os componentes de cor como valores de 16 bits (0-65535), por isso os componentes são deslocados 8 bits para a direita (>> 8) para serem convertidos para a faixa de 8 bits (0-255).

Desvantagens dessa abodagem

Embora o bitmasking seja extremamente eficiente em termos de desempenho e uso de memória, suas desvantagens em termos de complexidade, legibilidade e manutenção devem ser cuidadosamente consideradas.

  • Complexidade: Bitmasking pode ser confuso para programadores iniciantes ou para aqueles que não estão familiarizados com operações bitwise. A manipulação de bits diretamente exige uma compreensão sólida de operações binárias.
  • Legibilidade do Código: O código que utiliza bitmasking pode ser menos legível e intuitivo em comparação com outras abordagens. Por exemplo, verificar se um bit específico está definido pode não ser tão claro quanto verificar um campo booleano em uma estrutura de banco de dados.
  • Manutenção: Remover as opções ou modificar opções existentes pode ser propenso a erros, especialmente se não houver documentação adequada ou se os valores dos bits não forem gerenciados cuidadosamente.
  • Limitações de Tamanho: Dependendo do tipo de dado utilizado (por exemplo, int), há um limite no número de flags que podem ser representadas. Por exemplo, um int de 32 bits pode representar até 32 flags diferentes. Isso pode ser uma limitação em sistemas que necessitam de um grande número de opções.
  • Erros Silenciosos: Erros na manipulação de bits podem ser difíceis de diagnosticar e podem não resultar em falhas imediatas ou óbvias. Por exemplo, definir ou limpar o bit errado pode alterar inadvertidamente múltiplas flags, levando a comportamentos inesperados que podem ser difíceis de rastrear.

Conclusão

Bitmasking é uma técnica valiosa para representar e manipular conjuntos de opções de maneira eficiente. Em Go, essa técnica pode ser implementada de forma simples e eficaz, como demonstrado nos exemplos acima. Seja para sistemas de permissões, configurações de sistema ou estados de jogo, bitmasking oferece uma maneira poderosa de gerenciar múltiplas opções com operações bitwise rápidas e eficientes.

Para projetos onde a legibilidade e a facilidade de manutenção são prioridades, ou onde o número de opções é grande, outras técnicas, como estruturas de dados customizadas ou mapas, podem ser mais apropriadas. No entanto, para sistemas onde o desempenho é crítico e o número de opções é manejável, bitmasking continua sendo uma ferramenta poderosa e eficiente.

Se você está vindo de um background em PHP, C, Java ou qualquer outra linguagem, experimentar bitmasking em Go pode oferecer uma nova perspectiva, somando a eficiência e a simplicidade desta técnia ao arsenal de qualquer programador.

版本聲明 本文轉載於:https://dev.to/leonancarvalho/bitmasking-em-go-uma-tecnica-poderosa-para-gerenciamento-de-opcoes-2idb?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何在單擊按鈕時列印特定的 HTML 內容而不列印整個頁面?
    如何在單擊按鈕時列印特定的 HTML 內容而不列印整個頁面?
    在按鈕點擊時列印特定的HTML 內容而不包括完整網頁在使用者點擊按鈕時僅列印特定的HTML內容可以透過多種方式實現方式。一種方法是建立一個隱藏的 div 元素來保存所需的 HTML。為了列印目的,該 div 的顯示屬性應設定為“print”,而為了螢幕顯示,其顯示值應保持“none”。頁面上的其他元...
    程式設計 發佈於2024-11-05
  • 尋找經濟實惠的同日格蘭尼公寓(附 Pillar Build Granny Flats)
    尋找經濟實惠的同日格蘭尼公寓(附 Pillar Build Granny Flats)
    在 Pillar Build Granny Flats,我們為您提供祖母屋解決方案的精英服務,滿足您的獨特需求。無論是房主、承包商還是投資者,我們都可以幫助您在當天購買後院公寓,效果非常好,為您節省寶貴的時間,而且不用說,預算也很實惠。我們的祖母房建造者將在每一步工作,以確保您的專案以最精確和細心的...
    程式設計 發佈於2024-11-05
  • 如何使用 botoith Google Colab 和 AWS 集成
    如何使用 botoith Google Colab 和 AWS 集成
    您有沒有想過,在實施AWS Lambda時,想要一一確認程式碼的運作情況? 您可能認為在 AWS 控制台上實施很痛苦,因為您必須執行 Lambda 函數並且每次都會產生成本。 因此,我將向您展示您的擔憂的解決方案。 它是透過 Google Colab 和 AWS 整合實現的。 步驟如下: ...
    程式設計 發佈於2024-11-05
  • (高效能 Web 應用程式的要求
    (高效能 Web 應用程式的要求
    “高性能网络应用程序”或“前端”到底是什么? 自从 Internet Explorer 时代衰落以来,JavaScript 生态系统变得越来越强大,“前端”一词已成为高性能、现代 Web 客户端的代名词。这个“前端”世界的核心是 React。事实上,在前端开发中不使用 React 常常会让一个人看...
    程式設計 發佈於2024-11-05
  • 如何將單一輸入欄位設定為分區輸入?
    如何將單一輸入欄位設定為分區輸入?
    將輸入欄位設為分區輸入有多種方法可用於建立一系列分區輸入欄位。一種方法利用「字母間距」來分隔單一輸入欄位內的字元。此外,「background-image」和「border-bottom」樣式可以進一步增強多個輸入欄位的錯覺。 CSS Snippet以下 CSS 程式碼示範如何建立所需的效果:#pa...
    程式設計 發佈於2024-11-05
  • 用 Go 建構一個簡單的負載平衡器
    用 Go 建構一個簡單的負載平衡器
    负载均衡器在现代软件开发中至关重要。如果您曾经想知道如何在多个服务器之间分配请求,或者为什么某些网站即使在流量大的情况下也感觉更快,答案通常在于高效的负载平衡。 在这篇文章中,我们将使用 Go 中的循环算法构建一个简单的应用程序负载均衡器。这篇文章的目的是逐步了解负载均衡器的工作原理。 ...
    程式設計 發佈於2024-11-05
  • 如何以超連結方式開啟本機目錄?
    如何以超連結方式開啟本機目錄?
    透過超連結導航本地目錄嘗試在連結互動時啟動本地目錄視圖時,您可能會遇到限制。然而,有一個解決方案可以解決這個問題,並且可以在各種瀏覽器之間無縫運作。 實作方法因為從HTML 頁面直接開啟路徑或啟動瀏覽器是由於安全原因受到限制,更可行的方法是提供可下載的連結( .URL 或.LNK)。 建議路徑:.U...
    程式設計 發佈於2024-11-05
  • 為什麼 Makefile 會拋出 Go 指令的權限被拒絕錯誤?
    為什麼 Makefile 會拋出 Go 指令的權限被拒絕錯誤?
    在執行Go 時Makefile 中出現權限被拒絕錯誤透過Makefile 執行Go 指令時可能會遇到「權限被拒絕」錯誤,即使你可以直接執行它們。這種差異是由於 GNU make 中的問題引起的。 原因:當您的 PATH 上有一個目錄包含名為“go.gnu”的子目錄時,就會出現此錯誤。 ”例如,如果您...
    程式設計 發佈於2024-11-05
  • parseInt 函數中 Radix 參數的意義是什麼?
    parseInt 函數中 Radix 參數的意義是什麼?
    parseInt 函數中 Radix 的作用parseInt 函數將字串轉換為整數。然而,它並不總是採用以 10 為基數的數字系統。若要指定所需的基數,請使用基數參數。 理解基數基數是指單一數字表示的值的數量。例如,十六進制的基數為 16,八進制的基數為 8,二進制的基數為 2。 為什麼要用基數? ...
    程式設計 發佈於2024-11-05
  • 如何使用 JavaScript 將連結保留在同一選項卡中?
    如何使用 JavaScript 將連結保留在同一選項卡中?
    在同一分頁和視窗中導覽連結您可能會遇到想要在同一視窗和分頁中開啟連結的情況作為當前頁面。但是,使用 window.open 函數通常會導致在新分頁中開啟連結。為了解決這個問題,您可以使用name 屬性,如下所示:window.open("https://www.youraddress.co...
    程式設計 發佈於2024-11-05
  • 如何解決Python中的循環依賴?
    如何解決Python中的循環依賴?
    Python 中的循環依賴使用 Python 模組時遇到循環依賴可能是一個令人沮喪的問題。在這個特定場景中,我們有兩個文件,node.py 和 path.py,分別包含 Node 和 Path 類別。 最初,path.py 使用 from node.py import * 導入 node.py。但是...
    程式設計 發佈於2024-11-05
  • MariaDB 與 MySQL:開發人員需要了解什麼
    MariaDB 與 MySQL:開發人員需要了解什麼
    MariaDB 和 MySQL 是著名的開源 RDBMS,但儘管它們有著共同的歷史,但它們在功能和效能方面卻有所不同。本文快速強調了主要差異,幫助開發人員決定哪個資料庫最適合他們的需求。 差異和範例 儲存引擎,MariaDB 對 Aria 和 MyRocks 等引擎的擴充支援提供了...
    程式設計 發佈於2024-11-05
  • 為什麼我的 Goroutine 遞增變數會產生意外的結果?
    為什麼我的 Goroutine 遞增變數會產生意外的結果?
    這是編譯器最佳化的結果嗎? 在此程式碼片段中,啟動了一個 goroutine 並重複遞增變數 i:package main import "time" func main() { i := 1 go func() { for { ...
    程式設計 發佈於2024-11-05
  • 利用 AI 快速學習 Node.js - 第 4 天
    利用 AI 快速學習 Node.js - 第 4 天
    今天,借助ChatGPT繼續學習Node.js,重點是非同步程式設計。這是 Node.js 中最重要的概念之一,我很高興能夠開始掌握它。 理論 在 Node.js 中,非同步程式設計因其非阻塞、事件驅動的架構而至關重要。這意味著文件讀取、資料庫查詢或網路請求等操作在等待結果時不會阻塞其他程式碼的執...
    程式設計 發佈於2024-11-05
  • Java 可以定義帶有嵌入引號的字串而不轉義嗎?
    Java 可以定義帶有嵌入引號的字串而不轉義嗎?
    揭開Java 使用嵌入式引號定義字串的替代方法在Java 中處理字串時,您常常會在文字中遇到大量引號,導致繁瑣的轉義和可讀性挑戰。雖然其他語言提供了處理這種情況的語法,但 Java 缺乏類似的選項。 問題: Java 是否提供了另一種方法來定義帶有嵌入引號的字串而不訴諸轉義? 答案: 雖然Java ...
    程式設計 發佈於2024-11-05

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3