
import paged.*;
import paged.misc.*;

import vectors.generic.*;
import strings;
import io.mapping.*;
import io.paths.*;



//
// NamedVector[T]
//

record NamedVector[T] (
    fileName : strings.String,
    file : File,
    mapping : FileMapping,
    header : Pointer[Header],
    data : Pointer[T],
);

record Header (
    size : SizeT,
    capacity : SizeT,
);



//
// predicates
//

[T] overload RegularRecord?(static NamedVector[T]) = false;

[T] overload ContiguousSequence?(static NamedVector[T]) = true;



//
// constructor
//

private headerSpace() = alignUp(TypeSize(Header), 16u);

[T]
private requiredFileSize(static T, capacity) =
    capacity*TypeSize(T) + headerSpace();

[S,T | String?(S)]
overload NamedVector[T](fileName:S) {
    if (pathExists?(fileName)) {
        var f = File(fileName, READ_WRITE);
        var mapping = FileMapping(f, MAP_READ_WRITE);
        var header = Pointer[Header](mappingAddress(mapping));
        var data = Pointer[T](mappingAddress(mapping) + headerSpace());
        return NamedVector[T](strings.String(fileName), f, mapping, header, data);
    }
    else {
        var f = File(fileName, CREATE);
        var capacity = 16;
        var size = 0;
        // FIXME: not exception safe
        resizeFile(f, requiredFileSize(T, capacity));
        var mapping = FileMapping(f, MAP_READ_WRITE);
        var header = Pointer[Header](mappingAddress(mapping));
        var data = Pointer[T](mappingAddress(mapping) + headerSpace());
        header^.size = size;
        header^.capacity = capacity;
        return NamedVector[T](strings.String(fileName), f, mapping, header, data);
    }
}



//
// destroy
//

[T]
overload destroy(x:NamedVector[T]) {
    destroy(x.mapping);
    destroy(x.file);
    destroy(x.fileName);
}



//
// generic vector requirements
//

[T]
overload Vector?(static NamedVector[T]) = true;

[T]
overload vectorData(a:NamedVector[T]) = &(a.data^);

[T]
overload vectorSize(a:NamedVector[T]) = a.header^.size;

[T]
overload vectorSetSize(a:NamedVector[T], n:SizeT) {
    a.header^.size = n;
}

[T]
overload vectorCapacity(a:NamedVector[T]) = a.header^.capacity;

[T]
overload vectorRequestCapacity(a:NamedVector[T], capacity:SizeT) {
    assert(capacity >= vectorSize(a));
    if (capacity < vectorCapacity(a))
        return;
    var n = max(capacity, SizeT(16));
    n = max(n, 2*vectorCapacity(a));
    reallocVector(a, n);
}

[T]
private reallocVector(a:NamedVector[T], capacity:SizeT) {
    destroy(a.mapping);
    resizeFile(a.file, requiredFileSize(T, capacity));
    a.mapping <-- FileMapping(a.file, MAP_READ_WRITE);
    a.header = Pointer[Header](mappingAddress(a.mapping));
    a.data = Pointer[T](mappingAddress(a.mapping) + headerSpace());
    a.header^.capacity = capacity;
}
