[RUBY] Implement tail

I made something like tail that displays the last n lines of a file in Ruby

I will try to imitate.

Simple version

tail_simple.rb



file_path = ARGV[0]
row_limit = ARGV[1].to_i

buffer = []
File.foreach(file_path) do |line|
  buffer << line
  buffer.shift if buffer.size > row_limit
end

puts buffer

First, from the comment section of the original article. If you do it in Ruby, this is fine. This feature is

Such. It's surprisingly fast when I try it.

Buffer version

tail_buffered.rb



def reverse_chunks(file, size)
  n = file.size / size
  n -= 1 if file.size == n * size
  len = file.size - n * size
  until n < 0
    file.seek(n * size)
    yield file.read(len)
    n -= 1
    len = size
  end
end

def offset_of_nth_chr_from_tail(file, count, target)
  offset = 0
  reverse_chunks(file, 1024*16) do |chunk|
    chunk.size.times do |i|
      chr = chunk[chunk.size - i - 1]
      if chr == target || (offset == 0 && i == 0 && chr != target)
        count -= 1

        if count < 0
          offset += i
          return offset
        end
      end
    end
    offset += chunk.size
  end

  offset
end

def tail(fname, n_lines)
  open(fname) do |file|
    offset = offset_of_nth_chr_from_tail(file, n_lines, "\n")
    file.seek(file.size - offset)
    print file.read
  end
end

tail(ARGV[0], ARGV[1].to_i)

Next, I wrote a tail by Ruby. A method of dividing a file into chunks of a certain size and counting the number of line breaks from the back of the trailing chunk.

Not too early.

mmap version

Next is the implementation that if you are told to write tail, it will be in this direction.

tail_mmap.c



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

void tail(char *fname, int n_lines) {
  int fd = open(fname, O_RDONLY);
  struct stat st;
  fstat(fd, &st);
  int fsize = st.st_size;

  char *map = mmap(0, fsize, PROT_READ, MAP_PRIVATE, fd, 0);
  char *cursor = map+fsize;

  if (fsize > 0 && cursor[-1] != '\n') n_lines--;

  for (int i = 0; i < fsize; i++){
    cursor--;

    if (*cursor == '\n') n_lines--;

    if (n_lines < 0){
      cursor++;
      break;
    }
  }

  write(1, cursor, fsize-(cursor-map));

  munmap(map, fsize);
  close(fd);
}

int main(int argc, char *argv[]){
  if(argc != 3)  exit(EXIT_FAILURE);

  tail(argv[1], atoi(argv[2]));

  exit(EXIT_SUCCESS);
}

A method of counting from the back using mmap.

There are many merits. It's really fast. Faster than the original tail.

Recommended Posts

Implement tail
Implement Rails pagination
Let's implement Lexer (1)
Let's implement Lexer (2)
Let's implement EAM