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

Transfer Syntax index abstraction & inventory-less registry #17

Merged
merged 8 commits into from
Oct 24, 2019
Merged

Conversation

Enet4
Copy link
Owner

@Enet4 Enet4 commented Oct 6, 2019

This PR introduces a trait for transfer syntax dictionaries and makes it so that the main TS registry can work even without support for the inventory based registry. The inventory will be behind the Cargo feature inventory-registry, enabled by default only at the parent crate dicom and in end user tools such as dcmdump. If this is disabled, built-in TSes will still be available, but the registry will be unable to be extended. We can think of ways to overcome this issue in future contributions. This can be overcome by creating a new registry implementation.

This should address the main concern presented in #6, although I would be grateful to have this tested in that environment (CC @ibaryshnikov).

- trait for the TS registry
- implement it, adapt existing code
- `inventory-registry` feature for whether to include inventory-based TS
- make transfery-syntax-registry work even without inventory
   - built-in TSes no longer go through inventory
   - make entries.rs an actual module
- propagate feature to upper crates
   - make it default in parent crate and end tools such as dcmdump
- improve TS registry test to also check TS name
- run tests without inventory-based registry on a few crates
@ibaryshnikov
Copy link
Contributor

That's awesome, thanks for cc'ing, I'll be happy to test! I'm updating my prototype to fully use the code from this PR. I have one blocker though, DataElement at the moment only allows

    /// Retrieve the data value.
    pub fn value(&self) -> &Value<I> {
        &self.value
    }

Is it possible to add pub fn into_value(self) -> Value<I> { ? I need it to convert PrimitiveValue of PixelData into Vec<u16> in place. It's important, because inside a browser there's a 2Gb memory limit. As a .dcm file size is around 500Mb, it's easy to get out of memory error with any additional copy, especially after copying pixel data

@Enet4
Copy link
Owner Author

Enet4 commented Oct 7, 2019

Is it possible to add pub fn into_value(self) -> Value<I> { ?

Sure, I just pushed PR #18, which can be merged before this one.

@ibaryshnikov
Copy link
Contributor

@Enet4 would you mind rebasing it on master to grab the into_value update?

@ibaryshnikov
Copy link
Contributor

I'm running into another issue trying to update my code. I want to iterate through entries using into_iter, which is defined for InMemDicomObject and does self.entries.into_iter(). It's required because field self.entries is private.

    let root_object = from_reader(&input[128..])
        .map_err(|e| Error::new(&format!("Error parsing input buffer: {:?}", e)))?;

    for data_element in root_object.into_iter() {
        
    }

The error is cannot move out of dereference of RootDicomObject<InMemDicomObject<StandardDataDictionary>>

Since field obj in RootDicomObject is private, the only way to obtain it at the moment is to use deref

impl<T> ::std::ops::Deref for RootDicomObject<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.obj
    }
}

I guess, having something similar with into_value for RootDicomObject may help here

@ibaryshnikov
Copy link
Contributor

I tested this PR with

[dependencies]
dicom-core = "0.1.0"
dicom-object = "0.1.0"

and having a local checkout of this PR with minor changes

[patch.crates-io.dicom-core]
path = "../../../dicom-rs/core"
[patch.crates-io.dicom-object]
path = "../../../dicom-rs/object"

Both dicom-object = "0.1.0" and dicom-object = { version = "0.1.0", features = ["inventory-registry"] } work well in WebAssembly. I'd also like to test that while using feature inventory-registry it's not possible to extend TS registry in WebAssembly and possible to do it natively (which is expected behavior). I'll share the results after more tests tomorrow.

Changes I made in a local copy:

impl<T> RootDicomObject<T> {
    // not sure about a method name
    pub fn into_object(self) -> T {
        self.obj
    }
}

@Enet4
Copy link
Owner Author

Enet4 commented Oct 7, 2019

Just rebased. And yes, RootDicomObject is still a bit thin here. I just pushed another PR, #19, which should have what you are looking for. I called the method into_inner instead, as often employed for wrapper values. into_object might also be ambiguous, since RootDicomObject already behaves like an object.

Thank you for the testing and feedback so far!

@ibaryshnikov
Copy link
Contributor

Since #19 is merged, it'll be good to rebase this PR again.
Overal, I was able to test it with a local checkout
I used

[patch.crates-io.dicom-core]
path = "../../../dicom-rs/core"
[patch.crates-io.dicom-object]
path = "../../../dicom-rs/object"

All three options work fine for built-in transfer syntaxes

  • dicom-object = { version = "0.1.0" }
  • dicom-object = { version = "0.1.0", default-features = false }
  • dicom-object = { version = "0.1.0", features = ["inventory-registry"] }

I ran into one compiler error with this code

dicom-object = { version = "0.1.0", features = ["inventory-registry"] }
submit_transfer_syntax! {
    TransferSyntax::new(
        "1.2.840.10008.1.2.4.70",
        "JPEG Lossless, Non-Hierarchical, First-Order Prediction",
        Endianness::Little,
        true,
        Codec::EncapsulatedPixelData,
    )
}

The error is

error[E0282]: type annotations needed
 --> <::inventory::submit macros>:1:73
  |
1 | ($ ($ value : tt) *) => { $ crate :: r#impl :: submit ! { $ ($ value) * } }
  |                                                                         ^ cannot infer type for `A`

error: aborting due to previous error

- trait for the TS registry
- implement it, adapt existing code
- `inventory-registry` feature for whether to include inventory-based TS
- make transfery-syntax-registry work even without inventory
   - built-in TSes no longer go through inventory
   - make entries.rs an actual module
- propagate feature to upper crates
   - make it default in parent crate and end tools such as dcmdump
- improve TS registry test to also check TS name
- run tests without inventory-based registry on a few crates
@ibaryshnikov
Copy link
Contributor

Tested again with

[patch.crates-io.dicom-core]
git = "https://github.com/Enet4/dicom-rs"
rev = "2647055"
[patch.crates-io.dicom-object]
git = "https://github.com/Enet4/dicom-rs"
rev = "2647055"

Everything works fine for WebAssembly target:

  • dicom-object = { version = "0.1.0" }
  • dicom-object = { version = "0.1.0", default-features = false }
  • dicom-object = { version = "0.1.0", features = ["inventory-registry"] }

Surprisingly, I even saw some speedups in decoding)

@Enet4
Copy link
Owner Author

Enet4 commented Oct 8, 2019

The main TransferSyntax type is generic over an adapter type A, which means that your code still needs to disambiguate A. In most cases, the default A = DynDataRWAdapter works, but this is one situation where the compiler will choose not to infer to the default parameter type (see this SO question).

In practice, it's easier to create a static variable of the TS in a lazy_static context. Please see if something like the code below works.

lazy_static! {
    static ref MY_TS: TransferSyntax = TransferSyntax::new(
        "1.2.840.10008.1.2.4.70",
        "JPEG Lossless, Non-Hierarchical, First-Order Prediction",
        Endianness::Little,
        true,
        Codec::EncapsulatedPixelData,
    );
}
submit_transfer_syntax!(MY_TS);

(One shouldn't need to register "JPEG Lossless, Non-Hierarchical, First-Order Prediction" like this, it's already registered built-in. You would later on do this to implement pixel data encoding and decoding, which is yet to be designed at the moment.)

@ibaryshnikov
Copy link
Contributor

Thanks, I'll try it

One shouldn't need to register "JPEG Lossless, Non-Hierarchical, First-Order Prediction" like this, it's already registered built-in.

I know, I just commented it away in entries.rs and tried to submit it externally to test the submit_transfer_syntax macro )

- add `open_file_with` and `from_reader_with`
   - for extra control on which TS index to use
@Enet4
Copy link
Owner Author

Enet4 commented Oct 16, 2019

I have just pushed another commit which includes a way to open DICOM objects using another TS registry, which means that in theory, even in an environment without inventory, you could build your own collection of supported transfer syntaxes and pass that to the constructors.

@ibaryshnikov
Copy link
Contributor

ibaryshnikov commented Oct 21, 2019

Sorry for a delay. As default transfer syntaxes work in webassembly, I'm just testing that extension of TS registry works as expected for native target.

@ibaryshnikov
Copy link
Contributor

@Enet4 I tested all the cases I wanted: ts extension works fine for native target and doesn't work for webassembly (as expected). I haven't tested external TS registries though, but this change looks pretty straightforward. I think, it's good to go now. Thank you for updates!

PS, I figured out how to submit transfer syntax properly:

submit_transfer_syntax! {
    TransferSyntax::<NeverAdapter>::new(
        "1.2.840.10008.1.2.4.70",
        "JPEG Lossless, Non-Hierarchical, First-Order Prediction",
        Endianness::Little,
        true,
        Codec::EncapsulatedPixelData,
    )
}

The key part here is NeverAdapter, after I added it the error cannot infer type for `A` disappeared, therefore no need to use lazy_static

@Enet4
Copy link
Owner Author

Enet4 commented Oct 24, 2019

Glad to know you have managed to work around the new features. Yes, extending a WebAssembly environment to support more TSes would require building your own registry for the time being. The encoding crate could be extended to include a basic implementation in the future.

Thank you very much for testing this!

@Enet4 Enet4 merged commit 0c8bc46 into master Oct 24, 2019
@Enet4 Enet4 deleted the ts-index branch October 24, 2019 15:16
@Enet4 Enet4 mentioned this pull request Oct 27, 2019
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

Successfully merging this pull request may close these issues.

None yet

2 participants