package Bot::Cobalt::Plugin::Calc::Worker;
$Bot::Cobalt::Plugin::Calc::Worker::VERSION = '0.004003';
use strict;
use bytes;

use BSD::Resource qw/
  setrlimit

  RLIMIT_DATA
  RLIMIT_STACK
  RLIMIT_AS
  RLIMIT_VMEM
  RLIMIT_CPU
/;

use Storable 'nfreeze', 'thaw';

use Math::Calc::Parser ();

sub MEMLIMIT_BYTES () {  64 * 1024 * 1024 }

$SIG{INT} = sub { die "Timed out!\n" };

sub worker {
  binmode *STDOUT; binmode *STDIN;
  select *STDOUT;
  $|++;
 
  # not error-checked, may fail silently on some platforms ...
  setrlimit(RLIMIT_DATA, MEMLIMIT_BYTES, MEMLIMIT_BYTES);
  setrlimit(RLIMIT_STACK, MEMLIMIT_BYTES, MEMLIMIT_BYTES);
  setrlimit(RLIMIT_AS, MEMLIMIT_BYTES, MEMLIMIT_BYTES);
  setrlimit(RLIMIT_VMEM, MEMLIMIT_BYTES, MEMLIMIT_BYTES);
  setrlimit(RLIMIT_CPU, 10, 10);

  my ($buf, $read_bytes) = '';
  while (1) {
    if (defined $read_bytes) {
      if (length $buf >= $read_bytes) {
        my $input = thaw substr $buf, 0, $read_bytes, '';
        $read_bytes = undef;

        my ($tag, $expr) = @$input;

        my $result = Math::Calc::Parser->try_evaluate($expr);
        $result //= "err: ".Math::Calc::Parser->error;

        my $frozen = nfreeze [ $tag, $result ];
        my $stream  = length($frozen) . chr(0) . $frozen ;
        die $! unless syswrite(*STDOUT, $stream) == length $stream;
        exit 0
      }
    } elsif ($buf =~ s/^(\d+)\0//) {
      $read_bytes = $1;
      next
    }

    my $readb = sysread *STDIN, $buf, 4096, length $buf;
    last unless $readb;
  }

  exit 0
}

1;
