Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clearly define the behavior for package entry compression #56

Open
javagl opened this issue Aug 14, 2023 · 0 comments
Open

Clearly define the behavior for package entry compression #56

javagl opened this issue Aug 14, 2023 · 0 comments

Comments

@javagl
Copy link
Contributor

javagl commented Aug 14, 2023

Some questions about the compression of tileset package entries are still open. This refers to the specification as well as the implementation.


Regarding the specification:

  • Details about the entry compression may have to be added to the 3DTILES specification proposal at A proposal for a 3D Tiles packaging format specification 3d-tiles#727

  • The 3TZ specification at https://github.com/erikdahlstrom/3tz-specification/ already covers the entry compression, and says

    For optimal read performance, files in the archive should be stored without compression, however, for a good trade of read performance and file size, use the Zstandard compression method. For best compatibility with legacy software, choose the standard deflate compression method, noting that this is the slowest of the three methods.

    (The question of whether entries may be compressed with other methods - e.g. with GZIP - remains unanswered here)


Regarding the implementation:

The implementation here (i.e. the TilesetSource and TilesetTarget interface) is supposed to handle different package types (and the file system) as transparently as possible. This does raise questions about the intended behavior. For example, with (pseudocode) lines like

// Read the "tileset.json":
const buffer = source.read("tileset.json");

// Write the "tileset.json"
target.write("tileset.json", buffer);

it is not clear at which point the buffer may contain compressed data, how to detect the compression method when reading it, or how to define the compression method when writing it.


For many use-cases, it would be nice if the client didn't have to care about compression. This could largely be achieved by establishing a simple contract for the TilesetSource and TilesetTarget:

  • When reading data from a source, the resulting data will always be returned in its UNcompressed form
  • When passing data to a target, then the given data should always be given in its UNcompressed form

This would allow very convenient handling of compression on the API level.

When reading data, then the responsibility for detecting the compression method and uncompressing the data could solely be in the TilesetSource implementation.

This is important. If the client had to do something like

let buffer= source.read("tileset.json");
if (compressionMethodOf(buffer) === "gzip") {
    buffer = gzip.uncompress(buffer);
} else if (compressionMethodOf(buffer) === "zstd") {
    buffer = zstd.uncompress(buffer);
} else if (compressionMethodOf(buffer) === "brotli") {
    buffer = brotli.uncompress(buffer);
} else {
    // May be DEFLATE - this cannot be detected
    let uncompressed = undefined;
    try {
        uncompressed = deflate.uncompress(buffer);
    } catch (e) {
        // Nope, that wasn't deflate...
    }
    return buffer;
}

then this would require the client to detect the compression methods and (imprtant:) integrate all the compression libraries as dependencies.

The client should be able to just do a

const buffer = source.read("tileset.json");

and be done.

When writing data, then the responsibility for compressing the data could solely be in the TilesetTarget implementation. It would be trivial to have code like

// Write uncompressed data
const target = createTarget();
target.write("tileset.json", buffer);

// Write compressed data by wrapping the target into a compressing one
const compressingTarget = Targets.wrapGzip(target);
compressingTarget.write("tileset.json", buffer);

There may be cases where the client would like to access the compressed data. For example: When the data from a source is supposed to be served over the network, then the client may want to access the compressed data (if it is compressed with a method that can be used as the Content-Encoding).

This may make it necessary to add the corresponding functionality to the TilesetSource, like

// By default, always read uncompressed data
const uncompressedData = source.read("tileset.json");

// But allow determining the compression method
const method = source.getMethod("tileset.json"); // Can return "gzip" or "zstd" or... (what else?)

// Read the "raw" (compressed) data
const compressedData = source.readRaw("tileset.json");

But the decision here will depend on the decisions that are made on the specification level.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant