Data-RoaringBitmap-Shared

 view release on metacpan or  search on metacpan

t/03-frozen.t  view on Meta::CPAN


# ---- refuse a read-write reopen of a sealed file ----
like exception(sub { Data::RoaringBitmap::Shared->new($path, 256) }),
     qr/frozen|read-only/, 'read-write reopen of a sealed file is refused';

# ---- new_readonly rejects a non-frozen file ----
{
    my $u = "$dir/unsealed.rb";
    { my $rb = Data::RoaringBitmap::Shared->new($u, 256); $rb->add(7); }
    like exception(sub { Data::RoaringBitmap::Shared->new_readonly($u) }),
         qr/not frozen/, 'new_readonly on an unsealed file croaks';
}

# ---- new_readonly error paths ----
like exception(sub { Data::RoaringBitmap::Shared->new_readonly("$dir/does-not-exist.rb") }),
     qr/open|No such/, 'new_readonly on a missing path croaks';
like exception(sub { Data::RoaringBitmap::Shared->new_readonly(undef) }),
     qr/required/, 'new_readonly requires a path';

# ---- union FROM a frozen (read-only) other into a mutable bitmap (lock-free o read) ----
# Exercises copying an array container AND OR-ing a bitmap container out of a
# PROT_READ mapping while only the receiver is write-locked.
{
    my $m   = "$dir/other.rb";
    my $B   = Data::RoaringBitmap::Shared->new($m, 256);
    $B->add_many([@dense]);       # bucket 0 -> bitmap container
    $B->add($_) for @sparse;      # bucket 5 -> array container
    $B->freeze;
    my $Bro = Data::RoaringBitmap::Shared->new_readonly($m);

    my $A = Data::RoaringBitmap::Shared->new(undef, 256);
    $A->add(10);                  # bucket 0 (array) -- overlaps Bro's dense bitmap
    $A->add($B10 | 100);          # bucket 10 (array) -- absent from Bro
    my $ret = $A->union($Bro);    # must read Bro's containers without locking its PROT_READ map
    is $ret, $A, 'union returns the receiver for chaining';

    ok $A->contains(10),          'union target keeps its own array member';
    ok $A->contains($B10 | 100),  'union target keeps its own bucket Bro lacks';
    ok $A->contains(0),           'union pulled in Bro min (bitmap container)';
    ok $A->contains(5000),        'union pulled in Bro dense top (bitmap container)';
    ok $A->contains($B5 | 40000), 'union pulled in Bro sparse (array container)';
    is $A->cardinality, scalar(keys %{{ map { $_ => 1 } (10, $B10|100, @dense, @sparse) }}),
       'union cardinality is the set union of both';

    # ---- intersect FROM a frozen (read-only) other ----
    my $E = Data::RoaringBitmap::Shared->new(undef, 256);
    $E->add_many([0 .. 3000]);    # bucket 0 array, overlaps Bro's bitmap
    $E->add($B5 | 1);             # bucket 5 array, in Bro
    $E->add(900000);              # bucket 13, absent from Bro -> dropped
    $E->intersect($Bro);          # array(E) & bitmap(Bro), array & array, drop-missing-bucket
    ok  $E->contains(0),          'intersect kept a shared bitmap-side member';
    ok  $E->contains(3000),       'intersect kept the shared dense top';
    ok  $E->contains($B5 | 1),    'intersect kept a shared array member';
    ok !$E->contains(900000),     'intersect dropped a member absent from the frozen other';
    ok !$E->contains(4000),       'intersect dropped a member above the overlap';
    is $E->cardinality, 3002,     'intersect cardinality = |0..3000| + 1 shared array member';
}

done_testing;

# minimal exception helper (avoid a Test::Fatal dependency)
sub exception {
    my $code = shift;
    my $err;
    { local $@; eval { $code->(); 1 } or $err = $@; }
    return $err;
}



( run in 1.224 second using v1.01-cache-2.11-cpan-54e63673c56 )