Antes de enviar uma solicitação de suporte, leia atentamente esta leitura e verifique se já existe uma resposta entre as perguntas respondidas anteriormente: Não abuse do rastreador de problemas do GitHub.
A própria biblioteca possui um consumo implícito de memória de cerca de 0,5kb : 580 bytes (max) de código e 8 bytes de memória, de acordo com meus cálculos. Isso não considera o espaço usado para armazenar os próprios itens, obviamente.
Ao declarar seu buffer, você deve especificar o tipo de dados que ele deve manipular e a capacidade do buffer: esses dois parâmetros influenciarão a memória consumida pelo buffer.
# include < CircularBuffer.hpp >
CircularBuffer<byte, 100 > bytes; // uses 538 bytes
CircularBuffer< int , 100 > ints; // uses 638 bytes
CircularBuffer< long , 100 > longs; // uses 838 bytes
CircularBuffer< float , 100 > floats; // uses 988 bytes
CircularBuffer< double , 100 > doubles; // uses 988 bytes
CircularBuffer< char , 100 > chars; // uses 538 bytes
CircularBuffer< void *, 100 > pointers; // uses 638 bytes
Observação : o uso da memória relatado acima inclui a memória do programa usada pelo código da biblioteca, a memória da heap é muito menor e é comparável a uma matriz do mesmo tamanho e tipo do buffer.
Vamos começar a deixar as coisas claras: a biblioteca não suporta a inserção de dados no meio do buffer. Você pode adicionar dados ao buffer antes do primeiro elemento por meio de uma operação unshift()
ou após o último elemento por meio de uma operação push()
. Você pode continuar adicionando dados além da capacidade máxima do buffer, mas perderá as informações menos significativas:
unshift()
aumenta a cabeça , adicionar além da capacidade faz com que o elemento na cauda seja substituído e perdidopush()
adiciona à cauda , adicionar além da capacidade faz com que o elemento na cabeça seja substituído e perdido Tanto unshift()
quanto push()
retornam true
se a adição não causar nenhuma perda de informação, false
se ocorreu uma substituição:
CircularBuffer< int , 5 > buffer; // buffer capacity is 5
// all of the following return true
buffer.unshift( 1 ); // [1]
buffer.unshift( 2 ); // [2,1]
buffer.unshift( 3 ); // [3,2,1]
buffer.push( 0 ); // [3,2,1,0]
buffer.push( 5 ); // [3,2,1,0,5]
// buffer is now at full capacity, from now on any addition returns false
buffer.unshift( 2 ); // [2,3,2,1,0] returns false
buffer.unshift( 10 ); // [10,2,3,2,1] returns false
buffer.push(- 5 ); // [2,3,2,1,-5] returns false
Da mesma forma que a adição de dados, a recuperação de dados pode ser realizada no Tail através de uma operação pop()
ou da cabeça por meio de uma operação shift()
: Ambos fazem com que o elemento que seja lido seja removido do buffer.
A leitura de dados além do tamanho real do buffer tem um comportamento indefinido e é responsabilidade do usuário de impedir essas violações de limites usando as operações adicionais listadas na próxima seção. A biblioteca se comportará de maneira diferente, dependendo do tipo de dados e do método de alocação, mas você pode assumir com segurança que seu programa falhará se não assistir suas etapas.
As operações de leitura não destrutivas também estão disponíveis:
first()
retorna o elemento na cabeçalast()
retorna o elemento na cauda[]
CircularBuffer< char , 50 > buffer; // ['a','b','c','d','e','f','g']
buffer.first(); // ['a','b','c','d','e','f','g'] returns 'a'
buffer.last(); // ['a','b','c','d','e','f','g'] returns 'g'
buffer.pop(); // ['a','b','c','d','e','f'] returns 'g'
buffer.pop(); // ['a','b','c','d','e'] returns 'f'
buffer.shift(); // ['b','c','d','e'] returns 'a'
buffer.shift(); // ['c','d','e'] returns 'b'
buffer[ 0 ]; // ['c','d','e'] returns 'c'
buffer[ 1 ]; // ['c','d','e'] returns 'd'
buffer[ 2 ]; // ['c','d','e'] returns 'e'
buffer[ 10 ]; // ['c','d','e'] returned value is unpredictable
buffer[ 15 ]; // ['c','d','e'] returned value is unpredictable
isEmpty()
retorna true
apenas se nenhum dado for armazenado no bufferisFull()
retorna true
se nenhum dado puder ser adicionado ao buffer sem causar sobrescrito/perda de dadossize()
retorna o número de elementos atualmente armazenados no buffer; Ele deve ser usado em conjunto com o operador []
para evitar violações de limite: o primeiro índice de elementos é sempre 0
(se o buffer não estiver vazio), o último índice de elementos é sempre size() - 1
available()
retorna o número de elementos que podem ser adicionados antes de saturar o buffercapacity()
retorna o número de elementos que o buffer pode armazenar, apenas para completar, pois é definido pelo usuário e nunca alterações removidas de 1.3.0
substituídas pela capacity
variável de membro somente leituraclear()
redefine todo o buffer para o seu estado inicial (preste atenção, se você tivesse alocado dinamicamente objetos em seu buffer, a memória usada por esse objeto não é liberada: iterar sobre o conteúdo do buffer e liberar objetos de acordo com o método de alocação)copyToArray(array)
copia o conteúdo do buffer para uma array
padrão. A matriz deve ser grande o suficiente para manter todos os elementos atualmente no buffer.copyToArray(conversionFn, array)
copia o conteúdo do buffer para uma array
padrão executando uma função em cada elemento, geralmente uma conversão de tipo. A matriz deve ser grande o suficiente para manter todos os elementos atualmente no buffer. A partir da versão 1.3.0
a biblioteca é capaz de detectar automaticamente qual tipo de dados deve ser usado para o índice com base na capacidade do buffer:
65535
, seu índice será um unsigned long
unsigned int
para buffers com uma capacidade declarada maior que 255
byte
vai ser suficienteAlém disso, você pode misturar os mesmos buffers de código com pequenos índices e buffers com índice normal: anteriormente isso não era possível.
CircularBuffer< char , 100 > optimizedBuffer; // reduced memory footprint, index type is uint8_t (a.k.a. byte)
CircularBuffer< long , 500 > normalBuffer; // standard memory footprint, index type is unit16_t (a.k.a. unsigned int)
CircularBuffer< int , 66000 > hugeBuffer; // extended memory footprint, index type is unit32_t (a.k.a. unsigned long)
Para obter a vantagem máxima da otimização acima, sempre que você precisar se referir ao índice de buffer, você deve usar o tipo mais apropriado: isso pode ser facilmente alcançado usando o especificador decltype
, como no exemplo a seguir:
// the iterator variable i is of the correct type, even if
// we don't know what's the buffer declared capacity
for (decltype(buffer):: index_t i = 0 ; i < buffer.size(); i++) {
avg += buffer[i] / buffer. size ();
}
Se preferir, você pode alias o tipo de índice e consultar esse alias:
using index_t = decltype(buffer):: index_t ;
for ( index_t i = 0 ; i < buffer.size(); i++) {
avg += buffer[i] / buffer. size ();
}
O seguinte se aplica a versões antes de 1.3.0
apenas.
Por padrão, a biblioteca usa índices unsigned int
, permitindo um máximo de 65535
itens, mas você raramente precisará de uma loja tão grande.
Você pode trocar os indexos da biblioteca para o tipo byte
que define a macro CIRCULAR_BUFFER_XS
antes da diretiva #include
: isso reduz a memória usada pela própria biblioteca em apenas 36
bytes, mas permite que você potencialmente espremer muito mais sempre que executar um acesso indexado, se Você faz algum, usando o tipo de dados menor.
# define CIRCULAR_BUFFER_XS
# include < CircularBuffer.h >
CircularBuffer< short , 100 > buffer;
void setup () { }
void loop () {
// here i should be declared of type byte rather than unsigned int
// in order to maximize the effects of the optimization
for (byte i = 0 ; i < buffer. size () - 1 ; i++) {
Serial. print (buffer[i]);
}
}
Observação : Este comutador macro força o buffer a usar um tipo de dados de 8 bits como índice interno, pois todos os seus buffers serão limitados a uma capacidade máxima de 255
.
A biblioteca ajuda a trabalhar com interrupções que definem o comutador de macro CIRCULAR_BUFFER_INT_SAFE
, que introduz o modificador volatile
na variável count
, tornando toda a biblioteca mais favorável ao preço de desativar algumas otimizações do compilador. A declaração #define
precisa ser colocada em algum lugar antes da declaração #include
:
# define CIRCULAR_BUFFER_INT_SAFE
# include < CircularBuffer.h >
CircularBuffer< unsigned long , 10 > timings;
void count () {
timings. push ( millis ());
}
void setup () {
attachInterrupt ( digitalPinToInterrupt ( 2 ), count, RISING);
}
void loop () {
Serial. print ( " buffer size is " ); Serial. println (timings. size ());
delay ( 250 );
}
Observe que isso não torna a interrupção da biblioteca segura , mas ajuda seu uso em firmas de interrupção.
Vários exemplos estão disponíveis na pasta examples
da biblioteca:
copyToArray()
. Se você usar esta biblioteca para armazenar objetos alocados dinamicamente, evite usar o método clear()
, pois isso não executará a reação de memória: você precisa iterar sobre o conteúdo do buffer e liberar memória de acordo com o método de alocação usado, por delete
(se Você usou new
) ou free
(em caso de malloc
):
while (!buffer.isEmpty()) {
// pick the correct one
delete buffer. pop ();
free (buffer. pop ());
}
O mesmo se aplica às operações pop()
e shift()
pois qualquer objeto alocado dinamicamente é destacado apenas do buffer, mas a memória que ele usa não é liberada automaticamente (consulte o exemplo do objeto.ino)
Record* record = new Record(millis(), sample); // a dynamically allocated object
buffer.push(record);
// somewhere else
if (!buffer.isEmpty()) {
Record* current = buffer. pop ();
Serial. println (current. value ());
delete current; // if you don't do this the object memory is lost!!!
}
copyToArray(array)
e copyToArray(array, convertFn)
.h
para .hpp
shift()
e pop()
[]
abort()
é específico do AVRA maioria das principais melhorias abaixo foi contribuída por Erlkoenig90: obrigado Niklas!
capacity()
em favor da capacity
de atributo constanteEventLogging
e Interrupts
CIRCULAT_BUFFER_XS
Macro Switch em favor da identificação do tipo de índice automáticoUINT32_MAX
)CIRCULAR_BUFFER_INT_SAFE
memset
ao limparclear()
pop()
corrigidapop()
e shift()
corrigidascapacity()
debug()
adicionada, desativada pelo pré-processador por padrão