من فضلك ، قبل تقديم طلب دعم ، اقرأ بعناية هذا ReadMe وتحقق مما إذا كانت هناك إجابة موجودة بالفعل بين الأسئلة التي تم الإجابة عليها مسبقًا: لا تقم بإساءة استخدام متتبع قضية Github.
تحتوي المكتبة نفسها على استهلاك ضمني للذاكرة يبلغ حوالي 0.5 كيلو بايت : 580 بايت (بحد أقصى) من الكود و 8 بايت من الذاكرة ، وفقًا لحساباتاتي. هذا لا يعتبر المساحة المستخدمة لتخزين العناصر نفسها ، من الواضح.
عند الإعلان عن المخزن المؤقت ، يجب عليك تحديد نوع البيانات الذي يجب أن يتعامل معه وسعة المخزن المؤقت: ستؤثر هاتان المعلمتان على الذاكرة التي يستهلكها المخزن المؤقت.
# 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
يرجى ملاحظة : يتضمن استخدام الذاكرة المذكور أعلاه ذاكرة البرنامج المستخدمة من قبل رمز المكتبة ، وذاكرة الكومة أقل بكثير ويمكن مقارنتها بمجموعة من نفس الحجم ونوع المخزن المؤقت.
لنبدأ في توضيح الأمور: لا تدعم المكتبة إدخال البيانات في منتصف المخزن المؤقت. يمكنك إضافة بيانات إلى المخزن المؤقت إما قبل العنصر الأول عبر عملية unshift()
أو بعد العنصر الأخير عبر عملية push()
. يمكنك الاستمرار في إضافة البيانات إلى ما وراء الحد الأقصى لسعة المخزن المؤقت ، لكنك ستفقد أقل المعلومات المهمة:
unshift()
يضيف إلى الرأس ، فإن الإضافة إلى ما وراء السعة يؤدي إلى كتابة العنصر في الذيل وفقدهpush()
يضيف إلى الذيل ، فإن إضافة ما وراء السعة يؤدي إلى كتابة العنصر في الرأس وفقدانه كل من unshift()
و push()
يعود true
إذا لم تتسبب الإضافة في أي فقدان المعلومات ، false
في حالة حدوث كتابة فوق:
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
على غرار إضافة البيانات ، يمكن إجراء استرجاع البيانات عند الذيل عبر عملية pop()
أو من الرأس عبر عملية shift()
: كلاهما يتسبب في قراءة العنصر لإزالته من المخزن المؤقت.
تحتوي قراءة البيانات التي تتجاوز حجم المخزن المؤقت الفعلي على سلوك غير محدد وهي مسؤولية المستخدم عن منع مثل هذه الانتهاكات الحدودية باستخدام العمليات الإضافية المدرجة في القسم التالي. ستتصرف المكتبة بشكل مختلف اعتمادًا على نوع البيانات وطريقة التخصيص ، ولكن يمكنك أن تفترض بأمان أن برنامجك سيتعطل إذا لم تشاهد خطواتك.
تتوفر عمليات القراءة غير المدمرة أيضًا:
first()
يعيد العنصر في الرأسlast()
يعيد العنصر في الذيل[]
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()
يرجع true
فقط إذا لم يتم تخزين أي بيانات في المخزن المؤقتisFull()
true
إذا لم يكن من الممكن إضافة أي بيانات إلى المخزن المؤقت دون التسبب في فقدان/فقدان البياناتsize()
إرجاع عدد العناصر المخزنة حاليًا في المخزن المؤقت ؛ يجب استخدامه بالاقتران مع المشغل []
لتجنب انتهاكات الحدود: فهرس العنصر الأول هو دائمًا 0
(إذا لم يكن المخزن المؤقت فارغًا) ، فإن مؤشر العنصر الأخير هو size() - 1
دائمًاavailable()
إرجاع عدد العناصر التي يمكن إضافتها قبل تشبع المخزن المؤقتcapacity()
إرجاع عدد العناصر التي يمكن أن يخزنها المخزن المؤقت ، لاكتمالها فقط لأنه يحدده المستخدم ولا يتم إزالته من 1.3.0
باستبداله capacity
متغيرة الأعضاء للقراءة فقطclear()
إعادة تعيين المخزن المؤقت بالكامل إلى حالته الأولية (انتبه رغم ذلك ، إذا كنت قد قمت بتخصيص كائنات مخصصة ديناميكيًا في المخزن المؤقت الخاص بك ، فإن الذاكرة المستخدمة من قبل هذا الكائن لا يتم إصدارها: تكرار على محتويات المخزن المؤقت والكائنات المفروضة وفقًا لطريقة التخصيص الخاصة بهم)copyToArray(array)
بنسخ محتويات المخزن المؤقت إلى array
صفيف قياسي. يجب أن يكون الصفيف كبيرًا بما يكفي لعقد جميع العناصر الموجودة حاليًا في المخزن المؤقت.copyToArray(conversionFn, array)
بنسخ محتويات المخزن المؤقت إلى array
صفيف قياسية تنفذ وظيفة على كل عنصر ، وعادة ما يكون تحويل النوع. يجب أن يكون الصفيف كبيرًا بما يكفي لعقد جميع العناصر الموجودة حاليًا في المخزن المؤقت. بدءًا من الإصدار 1.3.0
تكون المكتبة قادرة على اكتشاف نوع البيانات تلقائيًا يجب استخدامها للفهرس بناءً على سعة المخزن المؤقت:
65535
، فسيكون فهرسك unsigned long
unsigned int
للمخازن المؤقتة مع قدرة معلنة أكبر من 255
byte
سوف يكفيبالإضافة إلى ذلك ، يمكنك المزج في نفس المخازن المؤقتة الكود مع فهرس صغير ومؤسسات مع فهرس عادي: في السابق لم يكن ذلك ممكنًا.
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)
للحصول على أقصى ميزة للتحسين أعلاه ، في أي وقت تحتاج إلى الرجوع إلى فهرس المخزن المؤقت ، يجب عليك استخدام الأنسب نوعًا: يمكن تحقيق ذلك بسهولة باستخدام محدد decltype
، كما هو الحال في المثال التالي:
// 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 ();
}
إذا كنت تفضل ذلك ، فيمكنك الاسم المستعار نوع الفهرس والإشارة إلى هذا الاسم المستعار:
using index_t = decltype(buffer):: index_t ;
for ( index_t i = 0 ; i < buffer.size(); i++) {
avg += buffer[i] / buffer. size ();
}
ما يلي ينطبق على الإصدارات قبل 1.3.0
فقط.
بشكل افتراضي ، تستخدم المكتبة فهارس unsigned int
، مما يسمح بحد أقصى 65535
عنصرًا ، ولكن نادراً ما تحتاج إلى مثل هذا المتجر الضخم.
يمكنك تبديل فهارس المكتبة إلى نوع byte
الذي يحدد الماكرو CIRCULAR_BUFFER_XS
قبل التوجيه #include
: هذا يقلل من الذاكرة المستخدمة من قبل المكتبة نفسها بمقدار 36
بايت فقط ، ولكنه يسمح لك بالضغط أكثر من ذلك بكثير كلما قمت بإجراء وصول مفهرسة ، إذا أنت تفعل أي ، باستخدام نوع البيانات الأصغر.
# 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]);
}
}
يرجى ملاحظة : يفرض مفتاح الماكرو هذا المخزن المؤقت على استخدام نوع البيانات 8 بتات كفهرس داخلي ، لأن جميع المخازن المؤقتة سوف تقتصر على الحد الأقصى لسعة 255
.
تساعد المكتبة في العمل مع المقاطعات التي تحدد مفتاح Macro CIRCULAR_BUFFER_INT_SAFE
، والذي يقدم المعدل volatile
إلى متغير count
، مما يجعل المكتبة بأكملها أكثر ملائمة للمقاطعة بسعر تعطيل بعض التحسينات المترجمة. يجب وضع بيان #define
في مكان ما قبل بيان #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 );
}
يرجى ملاحظة أن هذا لا يجعل مكتبة مقاطعة آمنة ، ولكنها تساعد في استخدامها في المقاطعة التي تعتمد على الشركات.
تتوفر أمثلة متعددة في مجلد examples
من المكتبة:
copyToArray()
. إذا كنت تستخدم هذه المكتبة لتخزين الكائنات المخصصة ديناميكيًا ، فإن الامتناع عن delete
طريقة clear()
لأن ذلك لن يؤدي إلى تحديد تعامل الذاكرة: تحتاج كنت قد استخدمت new
) أو free
(في حالة malloc
):
while (!buffer.isEmpty()) {
// pick the correct one
delete buffer. pop ();
free (buffer. pop ());
}
ينطبق الشيء نفسه على العمليات pop()
و shift()
حيث يتم فصل أي كائن مخصص ديناميكيًا فقط عن المخزن المؤقت ، ولكن لا يتم إصدار الذاكرة التي يستخدمها تلقائيًا (انظر الكائن.
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)
و copyToArray(array, convertFn)
.h
إلى .hpp
shift()
و pop()
عمليات إساءة استخدام تعطل المخزن المؤقت[]
abort()
هو خاص AVRوقد ساهمت معظم التحسينات الرئيسية أدناه من قبل Erlkoenig90: شكرًا لك Niklas!
capacity()
لصالح capacity
نمة المثيل الثابتةEventLogging
Interrupts
CIRCULAT_BUFFER_XS
لصالح تحديد نوع الفهرس التلقائيUINT32_MAX
)CIRCULAR_BUFFER_INT_SAFE
memset
عند المقاصةclear()
pop()
ثابتةpop()
و shift()
ثابتةcapacity()
وظيفةdebug()
، معطل من قبل المعالج بشكل افتراضي