Hello, please stay a while. We have programs, signatures and coffee. Man, we're cool.

Returning members sign in

Interested in programming?

Create a ZeroZaku account

Sign up, it's free and only takes a moment to gain complete access to SOTW, hidden content, and other features.

Tags

tutorial javascript

Algorithms: Circular Buffer Print view

Image


Final Code being produced:
function CircularBuffer(nodeLength) {
    // Initialize private data fields
    var buffer = new Array(nodeLength),
        head = 0,
        tail = 0;
   
    // Set default data so that undefined is not returned
    for(var j = 0; j < nodeLength; j++) {
        buffer[j] = null;
    }

    // Closure
    return {
        /// @param node the node inserted into the queue
        enqueue: function(node) {
            buffer[tail++] = node;
           
            // Set tail pointer to 0 when at the end of the array
            tail %= buffer.length;
        },
        /// @return node the current node where head is set to
        dequeue: function() {
            var node = null;
           
            // Check if empty before returning nodes
            if (!this.empty()) {
                node = buffer[head];
               
                // Destroy the processed element
                buffer[head++] = null;
            }
           
            // Set head pointer to 0 when at the end of the array
            head %= buffer.length;
   
            return node;
        },
        /// @return node the current node where head points to
        peek: function() {
            return buffer[head];
        },
        /// @return boolean check if the queue is empty
        empty: function() {
            return head === tail;
        }
    };
};

The overall code may be daunting but the entire concept is simple as Wikipedia could explain:

Image


Circular Buffers are queues with similar FIFO (First-In-First-Out) processing; however, circular buffers accumulate a fixed amount of space and imitates infinite space by looping through and overriding its previous data slots as demonstrated by the example above.

// Initialize private data fields
    var buffer = new Array(nodeLength),
        head = 0,
        tail = 0;

We begin by initializing the private variables because we don't want direct access to the array as an ADT (Abstract Data Type) should be. The buffer is the array structure which will contain all items (a linked-list can also be used). head is a reading pointer for array and determines the current item to be dequeued; conversely, tail is a writing pointer for the array and determines the current position for the next item to be enqueued.

// Set default data so that undefined is not returned
    for(var j = 0; j < nodeLength; j++) {
        buffer[j] = null;
    }

Here, we want to initialize all of the buffer's slots beforehand because arrays default to undefined when initialized. We set each value to null by default because we'll be using arbitrary data types.

// Closure
    return {
        ...
    };

We return an object literal so that we create a closure. In this case, we use closures to make our variables private and all of returned functions public since Javascript does not come equipped with public and private accessor keywords.

/// @param node the node inserted into the queue
        enqueue: function(node) {
            buffer[tail++] = node;
           
            // Set tail pointer to 0 when at the end of the array
            tail %= buffer.length;
        },
   dequeue: function() {
            var node = null;
           
            // Check if empty before returning nodes
            if (!this.empty()) {
                node = buffer[head];
               
                // Destroy the processed element
                buffer[head++] = null;
            }
           
            // Set head pointer to 0 when at the end of the array
            head %= buffer.length;
   
            return node;
        }

Enqueue() and dequeue() go hand-in-hand and comprise the fundamental processes of a queue (Circular Buffer is a type of Queue). Enqueue adds new items to the end of the buffer and dequeue removes elements from the top of the buffer.

tail : item_no : buffer_length
0 : 0 : 4
1 : 1 : 4
2 : 2 : 4
3 : 3 : 4
0 : 4 : 4

The most interesting code is the use of the modulus operator here e.g. tail %= buffer.length which basically sets the tail pointer to zero when at the end of the buffer.

peek: function() {
            return buffer[head];
        },
        empty: function() {
            return head === tail;
        }

Some extra functions include peek and empty as part of the standard Queue methods. Peek checks the current item and empty checks if all items have been processed.

Testing with sample code
var init = function() {
    var queue = new CircularBuffer(3);
    queue.enqueue('foo');
    queue.enqueue('bar');
   
    while(!queue.empty()) {
        document.writeln(queue.dequeue());
    }
}();

Here's a sample self-initializing function that creates a Circular Buffer given a maximum of three items. The string literals: 'foo' and 'bar' are appended to the queue. The final loop displays the circular buffer in order as the items dequeue, resulting in:

foo bar

Conclusion
Circular Buffers aren't difficult to implement as an extension of Queues and are particularly useful for processing arbitrarily large of infinite amounts of data such as data streams. If you like Algorithms, try out the B-Tree Data Structure.
Image
Hidden rage. September 18, 2007

Image
Loss. September 21, 2007
User avatar
Hmmm.
Original Poster
Reputation point: 150
Rank Medide Tier 3
Posts 932
Joined Jul 18th 2009
Location Torrance, CA

Return to Tutorials & Source Codes

Who is online

Registered users: Bing [Bot], Exabot [Bot], Google [Bot]