[med-svn] [odil] 01/03: Imported Upstream version 0.7.2
Julien Lamy
lamy-guest at moszumanska.debian.org
Tue Aug 16 13:46:17 UTC 2016
This is an automated email from the git hooks/post-receive script.
lamy-guest pushed a commit to branch master
in repository odil.
commit 4a4dba294fbe6704732cd547a2f6544e76e9a6b6
Author: Julien Lamy <lamy at unistra.fr>
Date: Tue Aug 16 13:20:55 2016 +0200
Imported Upstream version 0.7.2
---
.travis.yml | 9 +-
CMakeLists.txt | 2 +-
src/odil/Association.cpp | 18 ++-
src/odil/AssociationParameters.cpp | 73 +++++----
src/odil/AssociationParameters.h | 13 ++
src/odil/FindSCP.cpp | 1 +
src/odil/GetSCP.cpp | 3 +-
src/odil/MoveSCP.cpp | 19 ++-
src/odil/MoveSCP.h | 7 +-
src/odil/Reader.cpp | 143 +++++++++--------
src/odil/Reader.h | 11 +-
src/odil/VR.cpp | 2 +-
src/odil/Writer.cpp | 4 +-
src/odil/dul/Transport.cpp | 23 ++-
src/odil/dul/Transport.h | 2 +
src/odil/json_converter.cpp | 144 +++++++++++------
src/odil/json_converter.h | 6 +-
src/odil/logging.cpp | 2 +-
src/odil/logging.h | 3 +-
src/odil/unicode.cpp | 139 ++++++++++++++++
src/odil/unicode.h | 5 +
tests/code/AssociationParameters.cpp | 10 ++
tests/code/Reader.cpp | 222 +++++++++++++++++---------
tests/code/json_converter.cpp | 51 ++++++
tests/code/unicode.cpp | 211 ++++++++++++++++++++++--
tests/wrappers/test_association_parameters.py | 7 +-
tests/wrappers/test_value.py | 6 +
wrappers/AssocationParameters.cpp | 10 +-
wrappers/Value.cpp | 14 ++
29 files changed, 891 insertions(+), 269 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 4f694f8..1c6a9ab 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,7 +22,6 @@ addons:
- libboost-test-dev
- liblog4cpp5-dev
- dcmtk
- - ninja-build
- cmake
- pkg-config
before_install:
@@ -31,7 +30,7 @@ before_install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew uninstall json-c; fi
# Boost is already installed with another version
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew unlink boost; brew install boost boost-python; fi
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install dcmtk icu4c jsoncpp log4cpp ninja; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install dcmtk icu4c jsoncpp log4cpp; fi
- pip install --user cpp-coveralls nose
- export PATH=$(python -c 'import site; print(site.getuserbase())')/bin:${PATH}
before_script:
@@ -42,9 +41,9 @@ before_script:
- CMAKE_CXX_FLAGS="-std=c++11"
- if [ "${CC}" = "gcc" ]; then CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} --coverage"; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig; fi
- - cmake -G Ninja -D CMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS}" -D CMAKE_BUILD_TYPE:STRING=Debug ../
+ - cmake -D CMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS}" -D CMAKE_BUILD_TYPE:STRING=Debug ../
script:
- - ninja
- - ../tests/run
+ - make
+ - ../tests/run --no-network
after_success:
- if [ "${CC}" = "gcc" ]; then coveralls --exclude examples --exclude tests --exclude-pattern '.*CMake[^/]+\.c(?:pp)?' --exclude-pattern "/usr/.*" --root=${SRC_DIR} --build-root ${BIN_DIR} > /dev/null; fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3d7bf3..9c34df5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8)
project("odil")
set(odil_MAJOR_VERSION 0)
set(odil_MINOR_VERSION 7)
-set(odil_PATCH_VERSION 1)
+set(odil_PATCH_VERSION 2)
set(odil_VERSION
${odil_MAJOR_VERSION}.${odil_MINOR_VERSION}.${odil_PATCH_VERSION})
diff --git a/src/odil/Association.cpp b/src/odil/Association.cpp
index cbc8bf3..001ab87 100644
--- a/src/odil/Association.cpp
+++ b/src/odil/Association.cpp
@@ -276,7 +276,18 @@ Association
if(data.pdu == NULL)
{
// We have rejected the request
- //return false;
+ if(!data.reject)
+ {
+ throw (*data.reject);
+ }
+ else
+ {
+ throw AssociationRejected(
+ Association::RejectedTransient,
+ Association::ULServiceProvderPresentationRelatedFunction,
+ Association::NoReasonGiven,
+ "No reject information");
+ }
}
else
{
@@ -446,6 +457,11 @@ Association
::send_message(
message::Message const & message, std::string const & abstract_syntax)
{
+ if(!this->is_associated())
+ {
+ throw Exception("Not associated");
+ }
+
auto const transfer_syntax_it =
this->_transfer_syntaxes_by_abstract_syntax.find(abstract_syntax);
if(transfer_syntax_it == this->_transfer_syntaxes_by_abstract_syntax.end())
diff --git a/src/odil/AssociationParameters.cpp b/src/odil/AssociationParameters.cpp
index 7017b5b..e152cee 100644
--- a/src/odil/AssociationParameters.cpp
+++ b/src/odil/AssociationParameters.cpp
@@ -27,6 +27,19 @@
namespace odil
{
+AssociationParameters::PresentationContext
+::PresentationContext(
+ uint8_t id,
+ std::string const & abstract_syntax,
+ std::vector<std::string> const & transfer_syntaxes,
+ bool scu_role_support, bool scp_role_support, Result result)
+: id(id), abstract_syntax(abstract_syntax), transfer_syntaxes(transfer_syntaxes),
+ scu_role_support(scu_role_support), scp_role_support(scp_role_support),
+ result(result)
+{
+ // Nothing else.
+}
+
bool
AssociationParameters::PresentationContext
::operator==(PresentationContext const & other) const
@@ -41,6 +54,22 @@ AssociationParameters::PresentationContext
);
}
+AssociationParameters::UserIdentity
+::UserIdentity()
+: type(UserIdentity::Type::None), primary_field(), secondary_field()
+{
+ // Nothing else.
+}
+
+AssociationParameters::UserIdentity
+::UserIdentity(
+ Type type, std::string const & primary_field,
+ std::string const & secondary_field)
+: type(type), primary_field(primary_field), secondary_field(secondary_field)
+{
+ // Nothing else.
+}
+
bool
AssociationParameters::UserIdentity
::operator==(UserIdentity const & other) const
@@ -73,13 +102,15 @@ AssociationParameters::UserIdentity
AssociationParameters
::AssociationParameters()
: _called_ae_title(""), _calling_ae_title(""), _presentation_contexts(),
- _user_identity({UserIdentity::Type::None, "", ""}), _maximum_length(16384)
+ _user_identity(), _maximum_length(16384)
{
// Nothing else.
}
AssociationParameters
::AssociationParameters(pdu::AAssociateRQ const & pdu)
+: _called_ae_title(""), _calling_ae_title(""), _presentation_contexts(),
+ _user_identity(), _maximum_length(16384)
{
this->set_called_ae_title(pdu.get_called_ae_title());
this->set_calling_ae_title(pdu.get_calling_ae_title());
@@ -103,19 +134,12 @@ AssociationParameters
pcs_parameters.reserve(pcs_pdu.size());
for(auto const & pc_pdu: pcs_pdu)
{
- AssociationParameters::PresentationContext pc_parameters;
-
- pc_parameters.id = pc_pdu.get_id();
- pc_parameters.abstract_syntax = pc_pdu.get_abstract_syntax();
- pc_parameters.transfer_syntaxes = pc_pdu.get_transfer_syntaxes();
-
auto const it = roles_map.find(pc_pdu.get_abstract_syntax());
- pc_parameters.scu_role_support =
- (it!=roles_map.end())?it->second.first:true;
- pc_parameters.scp_role_support =
- (it!=roles_map.end())?it->second.second:false;
-
- pcs_parameters.push_back(pc_parameters);
+ pcs_parameters.emplace_back(
+ pc_pdu.get_id(),
+ pc_pdu.get_abstract_syntax(), pc_pdu.get_transfer_syntaxes(),
+ (it!=roles_map.end())?it->second.first:true,
+ (it!=roles_map.end())?it->second.second:false);
}
this->set_presentation_contexts(pcs_parameters);
@@ -171,7 +195,7 @@ AssociationParameters
std::map<uint8_t, PresentationContext> pcs_request_map;
for(auto const & pc: pcs_request)
{
- pcs_request_map[pc.id] = pc;
+ pcs_request_map.insert({pc.id, pc});
}
auto const & pcs_pdu = pdu.get_presentation_contexts();
@@ -190,23 +214,16 @@ AssociationParameters
pcs_parameters.reserve(pcs_pdu.size());
for(auto const & pc_pdu: pcs_pdu)
{
- AssociationParameters::PresentationContext pc_parameters;
auto const & pc_request = pcs_request_map.at(pc_pdu.get_id());
-
- pc_parameters.id = pc_pdu.get_id();
- pc_parameters.abstract_syntax = pc_request.abstract_syntax;
- pc_parameters.transfer_syntaxes = { pc_pdu.get_transfer_syntax() };
-
auto const it = roles_map.find(pc_request.abstract_syntax);
- pc_parameters.scu_role_support =
- (it!=roles_map.end())?it->second.first:pc_request.scu_role_support;
- pc_parameters.scp_role_support =
- (it!=roles_map.end())?it->second.second:pc_request.scp_role_support;
-
- pc_parameters.result =
- static_cast<PresentationContext::Result>(pc_pdu.get_result_reason());
- pcs_parameters.push_back(pc_parameters);
+ pcs_parameters.emplace_back(
+ pc_pdu.get_id(),
+ pc_request.abstract_syntax,
+ std::vector<std::string>{ pc_pdu.get_transfer_syntax() },
+ (it!=roles_map.end())?it->second.first:pc_request.scu_role_support,
+ (it!=roles_map.end())?it->second.second:pc_request.scp_role_support,
+ static_cast<PresentationContext::Result>(pc_pdu.get_result_reason()));
}
this->set_presentation_contexts(pcs_parameters);
diff --git a/src/odil/AssociationParameters.h b/src/odil/AssociationParameters.h
index 185868a..19e88e7 100644
--- a/src/odil/AssociationParameters.h
+++ b/src/odil/AssociationParameters.h
@@ -39,6 +39,13 @@ public:
TransferSyntaxesNotSupported = 4,
};
+ PresentationContext(
+ uint8_t id,
+ std::string const & abstract_syntax,
+ std::vector<std::string> const & transfer_syntaxes,
+ bool scu_role_support, bool scp_role_support,
+ Result result=Result::NoReason);
+
/// @brief Identifier of the presentation context, must be odd.
uint8_t id;
@@ -74,6 +81,12 @@ public:
SAML = 4
};
+ UserIdentity();
+
+ UserIdentity(
+ Type type, std::string const & primary_field,
+ std::string const & secondary_field);
+
/// @brief Identity type.
Type type;
diff --git a/src/odil/FindSCP.cpp b/src/odil/FindSCP.cpp
index 97be4c3..b45e2b8 100644
--- a/src/odil/FindSCP.cpp
+++ b/src/odil/FindSCP.cpp
@@ -86,6 +86,7 @@ FindSCP
}
catch(odil::Exception const & e)
{
+ status_fields.add(registry::ErrorComment, {e.what()});
final_status = message::CFindResponse::UnableToProcess;
}
diff --git a/src/odil/GetSCP.cpp b/src/odil/GetSCP.cpp
index 75354a4..38508b9 100644
--- a/src/odil/GetSCP.cpp
+++ b/src/odil/GetSCP.cpp
@@ -114,8 +114,9 @@ GetSCP
final_status = e.status;
status_fields = e.status_fields;
}
- catch(odil::Exception const &)
+ catch(odil::Exception const & e)
{
+ status_fields.add(registry::ErrorComment, {e.what()});
final_status = message::CGetResponse::UnableToProcess;
}
diff --git a/src/odil/MoveSCP.cpp b/src/odil/MoveSCP.cpp
index ee26243..785492a 100644
--- a/src/odil/MoveSCP.cpp
+++ b/src/odil/MoveSCP.cpp
@@ -66,7 +66,21 @@ MoveSCP
{
message::CMoveRequest const request(message);
- auto move_association = this->_generator->get_association(request);
+ Association move_association;
+ try
+ {
+ move_association = this->_generator->get_association(request);
+ }
+ catch(odil::Exception const &)
+ {
+ message::CMoveResponse response(
+ request.get_message_id(),
+ message::CMoveResponse::RefusedMoveDestinationUnknown);
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+ return;
+ }
+
move_association.associate();
StoreSCU store_scu(move_association);
@@ -123,8 +137,9 @@ MoveSCP
final_status = e.status;
status_fields = e.status_fields;
}
- catch(odil::Exception const &)
+ catch(odil::Exception const & e)
{
+ status_fields.add(registry::ErrorComment, {e.what()});
final_status = message::CMoveResponse::UnableToProcess;
}
diff --git a/src/odil/MoveSCP.h b/src/odil/MoveSCP.h
index f47d6af..63004fd 100644
--- a/src/odil/MoveSCP.h
+++ b/src/odil/MoveSCP.h
@@ -34,7 +34,12 @@ public:
/// @brief Return the number of responses.
virtual unsigned int count() const =0;
- /// @brief Return the sub-association to send responses on.
+ /**
+ * @brief Return the sub-association to send responses on.
+ *
+ * If the move destination is unknown, an odil::Exception must be
+ * thrown.
+ */
virtual Association get_association(message::CMoveRequest const &) const =0;
};
diff --git a/src/odil/Reader.cpp b/src/odil/Reader.cpp
index 988acd9..6c06033 100644
--- a/src/odil/Reader.cpp
+++ b/src/odil/Reader.cpp
@@ -162,6 +162,39 @@ Reader
return Tag(group, element);
}
+uint32_t
+Reader
+::read_length(VR vr) const
+{
+ uint32_t length;
+ if(this->explicit_vr)
+ {
+ // PS 3.5, 7.1.2
+ if(is_binary(vr)
+ || vr == VR::SQ || vr == VR::UC || vr == VR::UR || vr == VR::UT)
+ {
+ Reader::ignore(this->stream, 2);
+ auto const vl = Reader::read_binary<uint32_t>(
+ this->stream, this->byte_ordering);
+ length = vl;
+ }
+ else
+ {
+ auto const vl = Reader::read_binary<uint16_t>(
+ this->stream, this->byte_ordering);
+ length = vl;
+ }
+ }
+ else
+ {
+ auto const vl = Reader::read_binary<uint32_t>(
+ this->stream, this->byte_ordering);
+ length = vl;
+ }
+
+ return length;
+}
+
Element
Reader
::read_element(Tag const & tag, DataSet const & data_set) const
@@ -179,7 +212,12 @@ Reader
Value value;
- if(vr == VR::IS || vr == VR::SL || vr == VR::SS ||
+ auto const vl = this->read_length(vr);
+ if(vl == 0)
+ {
+ value = Value();
+ }
+ else if(vr == VR::IS || vr == VR::SL || vr == VR::SS ||
vr == VR::UL || vr == VR::US)
{
value = Value(Value::Integers());
@@ -209,10 +247,17 @@ Reader
throw Exception("Cannot create value for VR " + as_string(vr));
}
- Visitor visitor(
- this->stream, vr, this->transfer_syntax, this->byte_ordering,
- this->explicit_vr, this->keep_group_length);
- apply_visitor(visitor, value);
+ if(!value.empty())
+ {
+ Visitor visitor(
+ this->stream, vr, vl, this->transfer_syntax, this->byte_ordering,
+ this->explicit_vr, this->keep_group_length);
+ apply_visitor(visitor, value);
+ if(vr == VR::SQ && value.as_data_sets().empty())
+ {
+ value = Value();
+ }
+ }
return Element(value, vr);
}
@@ -266,9 +311,10 @@ Reader
Reader::Visitor
::Visitor(
- std::istream & stream, VR vr, std::string const & transfer_syntax,
- ByteOrdering byte_ordering, bool explicit_vr, bool keep_group_length)
-: stream(stream), vr(vr), transfer_syntax(transfer_syntax),
+ std::istream & stream, VR vr, uint32_t vl,
+ std::string const & transfer_syntax, ByteOrdering byte_ordering,
+ bool explicit_vr, bool keep_group_length)
+: stream(stream), vr(vr), vl(vl), transfer_syntax(transfer_syntax),
byte_ordering(byte_ordering), explicit_vr(explicit_vr),
keep_group_length(keep_group_length)
{
@@ -279,11 +325,9 @@ Reader::Visitor::result_type
Reader::Visitor
::operator()(Value::Integers & value) const
{
- auto const vl = this->read_length();
-
if(this->vr == VR::IS)
{
- auto const string = read_string(this->stream, vl);
+ auto const string = read_string(this->stream, this->vl);
if(!string.empty())
{
auto const strings = this->split_strings(string);
@@ -299,11 +343,11 @@ Reader::Visitor
uint32_t items = 0;
if(this->vr == VR::SL || this->vr == VR::UL)
{
- items = vl/4;
+ items = this->vl/4;
}
else if(this->vr == VR::AT || this->vr == VR::SS || this->vr == VR::US)
{
- items = vl/2;
+ items = this->vl/2;
}
else
{
@@ -345,11 +389,9 @@ Reader::Visitor::result_type
Reader::Visitor
::operator()(Value::Reals & value) const
{
- auto const vl = this->read_length();
-
if(this->vr == VR::DS)
{
- auto const string = read_string(this->stream, vl);
+ auto const string = read_string(this->stream, this->vl);
if(!string.empty())
{
auto const strings = this->split_strings(string);
@@ -368,11 +410,11 @@ Reader::Visitor
uint32_t items = 0;
if(this->vr == VR::FD)
{
- items = vl/8;
+ items = this->vl/8;
}
else if(this->vr == VR::FL)
{
- items = vl/4;
+ items = this->vl/4;
}
else
{
@@ -419,8 +461,7 @@ Reader::Visitor
}
else
{
- auto const vl = this->read_length();
- auto const string = read_string(this->stream, vl);
+ auto const string = read_string(this->stream, this->vl);
if(this->vr == VR::LT || this->vr == VR::ST || this->vr == VR::UT)
{
value = { string };
@@ -448,12 +489,10 @@ Reader::Visitor::result_type
Reader::Visitor
::operator()(Value::DataSets & value) const
{
- auto const vl = this->read_length();
-
- if(vl != 0xffffffff)
+ if(this->vl != 0xffffffff)
{
// Explicit length sequence
- std::string const data = read_string(this->stream, vl);
+ std::string const data = read_string(this->stream, this->vl);
std::istringstream sequence_stream(data);
Reader const sequence_reader(
sequence_stream, this->transfer_syntax, this->keep_group_length);
@@ -505,12 +544,11 @@ Reader::Visitor::result_type
Reader::Visitor
::operator()(Value::Binary & value) const
{
- auto const vl = this->read_length();
- if(vl == 0)
+ if(this->vl == 0)
{
return;
}
- else if(vl == 0xffffffff)
+ else if(this->vl == 0xffffffff)
{
value = Reader::read_encapsulated_pixel_data(
this->stream, this->byte_ordering, this->transfer_syntax,
@@ -521,18 +559,18 @@ Reader::Visitor
value.resize(1);
if(this->vr == VR::OB || this->vr == VR::UN)
{
- value[0].resize(vl);
+ value[0].resize(this->vl);
this->stream.read(
reinterpret_cast<char*>(&value[0][0]), value[0].size());
}
else if(this->vr == VR::OD)
{
- if(vl%8 != 0)
+ if(this->vl%8 != 0)
{
throw Exception("Cannot read OD for odd-sized array");
}
- value[0].resize(vl);
+ value[0].resize(this->vl);
for(unsigned int i=0; i<value[0].size(); i+=8)
{
auto const item = Reader::read_binary<double>(
@@ -542,12 +580,12 @@ Reader::Visitor
}
else if(this->vr == VR::OF)
{
- if(vl%4 != 0)
+ if(this->vl%4 != 0)
{
throw Exception("Cannot read OF for odd-sized array");
}
- value[0].resize(vl);
+ value[0].resize(this->vl);
for(unsigned int i=0; i<value[0].size(); i+=4)
{
auto const item = Reader::read_binary<float>(
@@ -557,12 +595,12 @@ Reader::Visitor
}
else if(this->vr == VR::OL)
{
- if(vl%4 != 0)
+ if(this->vl%4 != 0)
{
throw Exception("Cannot read OL for odd-sized array");
}
- value[0].resize(vl);
+ value[0].resize(this->vl);
for(unsigned int i=0; i<value[0].size(); i+=4)
{
auto const item = Reader::read_binary<uint32_t>(
@@ -572,12 +610,12 @@ Reader::Visitor
}
else if(this->vr == VR::OW)
{
- if(vl%2 != 0)
+ if(this->vl%2 != 0)
{
throw Exception("Cannot read OW for odd-sized array");
}
- value[0].resize(vl);
+ value[0].resize(this->vl);
for(unsigned int i=0; i<value[0].size(); i+=2)
{
auto const item = Reader::read_binary<uint16_t>(
@@ -592,39 +630,6 @@ Reader::Visitor
}
}
-uint32_t
-Reader::Visitor
-::read_length() const
-{
- uint32_t length;
- if(this->explicit_vr)
- {
- if(vr == VR::OB || vr == VR::OD || vr == VR::OF || vr == VR::OL ||
- vr == VR::OW || vr == VR::OF || vr == VR::SQ || vr == VR::UC ||
- vr == VR::UR || vr == VR::UT || vr == VR::UN)
- {
- Reader::ignore(this->stream, 2);
- auto const vl = Reader::read_binary<uint32_t>(
- this->stream, this->byte_ordering);
- length = vl;
- }
- else
- {
- auto const vl = Reader::read_binary<uint16_t>(
- this->stream, this->byte_ordering);
- length = vl;
- }
- }
- else
- {
- auto const vl = Reader::read_binary<uint32_t>(
- this->stream, this->byte_ordering);
- length = vl;
- }
-
- return length;
-}
-
Value::Strings
Reader::Visitor
::split_strings(std::string const & string) const
diff --git a/src/odil/Reader.h b/src/odil/Reader.h
index 5ae85a5..e4193ce 100644
--- a/src/odil/Reader.h
+++ b/src/odil/Reader.h
@@ -73,6 +73,9 @@ public:
/// @brief Read a tag.
Tag read_tag() const;
+ /// @brief Read the length of an element.
+ uint32_t read_length(VR vr) const;
+
/**
* @brief Read an element (VR and value), try to guess the VR from the tag,
* partially read data set, and transfer syntax for implicit VR transfer
@@ -95,6 +98,7 @@ private:
std::istream & stream;
VR vr;
+ uint32_t vl;
std::string transfer_syntax;
ByteOrdering byte_ordering;
@@ -102,8 +106,9 @@ private:
bool keep_group_length;
Visitor(
- std::istream & stream, VR vr, std::string const & transfer_syntax,
- ByteOrdering byte_ordering, bool explicit_vr, bool keep_group_length);
+ std::istream & stream, VR vr, uint32_t vl,
+ std::string const & transfer_syntax, ByteOrdering byte_ordering,
+ bool explicit_vr, bool keep_group_length);
result_type operator()(Value::Integers & value) const;
result_type operator()(Value::Reals & value) const;
@@ -111,7 +116,7 @@ private:
result_type operator()(Value::DataSets & value) const;
result_type operator()(Value::Binary & value) const;
- uint32_t read_length() const;
+ // uint32_t read_length() const;
Value::Strings split_strings(std::string const & string) const;
DataSet read_item(std::istream & specific_stream) const;
diff --git a/src/odil/VR.cpp b/src/odil/VR.cpp
index eb76674..f127bd9 100644
--- a/src/odil/VR.cpp
+++ b/src/odil/VR.cpp
@@ -89,7 +89,7 @@ std::string as_string(VR vr)
}
catch(std::out_of_range const &)
{
- throw Exception("Unknown VR");
+ throw Exception("Unknown VR: "+std::to_string(static_cast<int>(vr)));
}
}
diff --git a/src/odil/Writer.cpp b/src/odil/Writer.cpp
index 55186b2..4d00dd8 100644
--- a/src/odil/Writer.cpp
+++ b/src/odil/Writer.cpp
@@ -175,8 +175,8 @@ Writer
if(this->explicit_vr)
{
if(vr == VR::OB || vr == VR::OD || vr == VR::OF || vr == VR::OL ||
- vr == VR::OW || vr == VR::OF || vr == VR::SQ || vr == VR::UC ||
- vr == VR::UR || vr == VR::UT || vr == VR::UN)
+ vr == VR::OW || vr == VR::SQ || vr == VR::UC || vr == VR::UR ||
+ vr == VR::UT || vr == VR::UN)
{
this->write_binary(uint16_t(0), this->stream, this->byte_ordering);
diff --git a/src/odil/dul/Transport.cpp b/src/odil/dul/Transport.cpp
index bd813c7..6b56f15 100644
--- a/src/odil/dul/Transport.cpp
+++ b/src/odil/dul/Transport.cpp
@@ -128,8 +128,11 @@ Transport
this->_start_deadline(source, error);
this->_socket = std::make_shared<Socket>(this->_service);
- boost::asio::ip::tcp::acceptor acceptor(this->_service, endpoint);
- acceptor.async_accept(
+ this->_acceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(
+ this->_service, endpoint);
+ boost::asio::socket_base::reuse_address option(true);
+ this->_acceptor->set_option(option);
+ this->_acceptor->async_accept(
*this->_socket,
[&source,&error](boost::system::error_code const & e)
{
@@ -139,19 +142,25 @@ Transport
);
this->_run(source, error);
+
+ this->_acceptor = nullptr;
}
void
Transport
::close()
{
- if(!this->is_open())
+ if(this->_acceptor && this->_acceptor->is_open())
{
- throw Exception("Not connected");
+ this->_acceptor->close();
+ this->_acceptor = nullptr;
+ }
+ if(this->is_open())
+ {
+ this->_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
+ this->_socket->close();
+ this->_socket = nullptr;
}
-
- this->_socket->close();
- this->_socket = nullptr;
}
std::string
diff --git a/src/odil/dul/Transport.h b/src/odil/dul/Transport.h
index 726ef87..8b9d046 100644
--- a/src/odil/dul/Transport.h
+++ b/src/odil/dul/Transport.h
@@ -87,6 +87,8 @@ private:
duration_type _timeout;
boost::asio::deadline_timer _deadline;
+ std::shared_ptr<boost::asio::ip::tcp::acceptor> _acceptor;
+
enum class Source
{
NONE,
diff --git a/src/odil/json_converter.cpp b/src/odil/json_converter.cpp
index 4ecd7d6..c9a970f 100644
--- a/src/odil/json_converter.cpp
+++ b/src/odil/json_converter.cpp
@@ -15,6 +15,8 @@
#include "odil/base64.h"
#include "odil/DataSet.h"
#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/unicode.h"
#include "odil/Value.h"
#include "odil/VR.h"
@@ -22,10 +24,17 @@ namespace odil
{
/// @brief Element visitor converting to JSON.
-struct ToJSONVisitor
+class ToJSONVisitor
{
+public:
typedef Json::Value result_type;
+ ToJSONVisitor(odil::Value::Strings const & specific_char_set)
+ : _specific_character_set(specific_char_set)
+ {
+ // Nothing else.
+ }
+
result_type operator()(VR const vr) const
{
result_type result;
@@ -71,53 +80,16 @@ struct ToJSONVisitor
if(vr == VR::PN)
{
- auto const fields = { "Alphabetic", "Ideographic", "Phonetic" };
-
for(auto const item: value)
{
- auto fields_it = fields.begin();
-
- Json::Value json_item;
-
- std::string::size_type begin=0;
- while(begin != std::string::npos)
- {
- std::string::size_type const end = item.find("=", begin);
-
- std::string::size_type size = 0;
- if(end != std::string::npos)
- {
- size = end-begin;
- }
- else
- {
- size = std::string::npos;
- }
-
- json_item[*fields_it] = item.substr(begin, size);
-
- if(end != std::string::npos)
- {
- begin = end+1;
- ++fields_it;
- if(fields_it == fields.end())
- {
- throw Exception("Invalid Person Name");
- }
- }
- else
- {
- begin = end;
- }
- }
- result["Value"].append(json_item);
+ result["Value"].append(this->_convert_pn(item));
}
}
else
{
for(auto const & item: value)
{
- result["Value"].append(item);
+ result["Value"].append(this->_convert_string(vr, item));
}
}
return result;
@@ -158,20 +130,98 @@ struct ToJSONVisitor
return result;
}
+private:
+ odil::Value::Strings _specific_character_set;
+
+ std::string _convert_string(VR const vr, Value::String const & value) const
+ {
+ if(
+ vr != VR::LO && vr != VR::LT &&
+ vr != VR::PN && vr != odil::VR::SH &&
+ vr != VR::ST && vr != VR::UT)
+ {
+ // Nothing to do
+ return value;
+ }
+
+ return as_utf8(value, this->_specific_character_set, vr==VR::PN);
+ }
+
+ Json::Value _convert_pn(odil::Value::String const & value) const
+ {
+ static auto const fields = { "Alphabetic", "Ideographic", "Phonetic" };
+
+ Json::Value json;
+ auto fields_it = fields.begin();
+
+ std::string::size_type begin=0;
+ while(begin != std::string::npos)
+ {
+ std::string::size_type const end = value.find("=", begin);
+
+ std::string::size_type size = 0;
+ if(end != std::string::npos)
+ {
+ size = end-begin;
+ }
+ else
+ {
+ size = std::string::npos;
+ }
+
+ auto const component = value.substr(begin, size);
+ json[*fields_it] = this->_convert_string(odil::VR::PN, component);
+
+ if(end != std::string::npos)
+ {
+ begin = end+1;
+ ++fields_it;
+ if(fields_it == fields.end())
+ {
+ throw Exception("Invalid Person Name");
+ }
+ }
+ else
+ {
+ begin = end;
+ }
+ }
+
+ return json;
+ }
};
-Json::Value as_json(DataSet const & data_set)
+Json::Value as_json(
+ DataSet const & data_set,
+ odil::Value::Strings const & specific_character_set)
{
+ auto current_specific_char_set = specific_character_set;
+
Json::Value json;
for(auto const & it: data_set)
{
auto const & tag = it.first;
+ if(tag.element == 0)
+ {
+ // Skip group length tags
+ continue;
+ }
+
auto const & element = it.second;
+ // Specific character set
+ if(tag == registry::SpecificCharacterSet)
+ {
+ current_specific_char_set = element.as_string();
+ }
+
std::string const key(tag);
- auto const value = apply_visitor(ToJSONVisitor(), element);
+
+ ToJSONVisitor const visitor(current_specific_char_set);
+ auto const value = apply_visitor(visitor, element);
+
json[key] = value;
}
@@ -191,10 +241,7 @@ DataSet as_dataset(Json::Value const & json)
Element element;
- if(vr == VR::AE || vr == VR::AS || vr == VR::AT || vr == VR::CS ||
- vr == VR::DA || vr == VR::DT || vr == VR::LO || vr == VR::LT ||
- vr == VR::SH || vr == VR::ST || vr == VR::TM || vr == VR::UI ||
- vr == VR::UT)
+ if(odil::is_string(vr) && vr != odil::VR::PN)
{
element = Element(Value::Strings(), vr);
@@ -229,7 +276,7 @@ DataSet as_dataset(Json::Value const & json)
element.as_string().push_back(dicom_item);
}
}
- else if(vr == VR::DS || vr == VR::FD || vr == VR::FL)
+ else if(is_real(vr))
{
element = Element(Value::Reals(), vr);
@@ -239,8 +286,7 @@ DataSet as_dataset(Json::Value const & json)
element.as_real().push_back(json_item.asDouble());
}
}
- else if(vr == VR::IS || vr == VR::SL || vr == VR::SS ||
- vr == VR::UL || vr == VR::US)
+ else if(is_int(vr))
{
element = Element(Value::Integers(), vr);
diff --git a/src/odil/json_converter.h b/src/odil/json_converter.h
index f6df7f2..f4bcab7 100644
--- a/src/odil/json_converter.h
+++ b/src/odil/json_converter.h
@@ -10,13 +10,17 @@
#define _6f5dc463_a89a_4f77_a0ed_36dca74b9e59
#include <json/json.h>
+
#include "odil/DataSet.h"
+#include "odil/Value.h"
namespace odil
{
/// @brief Convert a data set to its JSON representation.
-Json::Value as_json(DataSet const & data_set);
+Json::Value as_json(
+ DataSet const & data_set,
+ odil::Value::Strings const & specific_character_set=odil::Value::Strings());
/// @brief Create a data set from its JSON representation.
DataSet as_dataset(Json::Value const & json);
diff --git a/src/odil/logging.cpp b/src/odil/logging.cpp
index 52e9626..674bd0d 100644
--- a/src/odil/logging.cpp
+++ b/src/odil/logging.cpp
@@ -23,7 +23,7 @@ bool configure()
auto * appender = new log4cpp::OstreamAppender("console", &std::cout);
appender->setLayout(new log4cpp::BasicLayout());
- auto & root = log4cpp::Category::getRoot();
+ auto & root = log4cpp::Category::getInstance("odil");
root.setPriority(log4cpp::Priority::WARN);
root.addAppender(appender);
diff --git a/src/odil/logging.h b/src/odil/logging.h
index 21b2677..a9b9712 100644
--- a/src/odil/logging.h
+++ b/src/odil/logging.h
@@ -12,6 +12,7 @@
#include <log4cpp/Category.hh>
#include <log4cpp/Priority.hh>
-#define ODIL_LOG(level) log4cpp::Category::getRoot() << log4cpp::Priority::level
+#define ODIL_LOG(level) \
+ log4cpp::Category::getInstance("odil") << log4cpp::Priority::level
#endif // _5382f5e0_e993_4966_9447_542844edb635
diff --git a/src/odil/unicode.cpp b/src/odil/unicode.cpp
index b971423..498b5dc 100644
--- a/src/odil/unicode.cpp
+++ b/src/odil/unicode.cpp
@@ -16,6 +16,7 @@
#include <string>
#include <unicode/errorcode.h>
+#include <unicode/ucnv.h>
#include <unicode/unistr.h>
#include "odil/Exception.h"
@@ -189,6 +190,47 @@ std::string as_utf8(
return encoded;
}
+std::string as_specific_character_set(
+ std::string::const_iterator const begin, std::string::const_iterator const end,
+ std::string const & encoding)
+{
+ UErrorCode error_code=U_ZERO_ERROR;
+
+ UConverter * converter = ucnv_open(
+ encoding.empty()?"ascii":encoding.c_str(), &error_code);
+ if(U_FAILURE(error_code))
+ {
+ throw Exception(
+ "Could not open converter '"+encoding+"': "+u_errorName(error_code));
+ }
+
+ auto const source_size = end-begin;
+
+ auto const target_capacity = 4*source_size;
+ char * target = new char[target_capacity];
+
+ auto const target_size = ucnv_fromAlgorithmic(
+ converter, UCNV_UTF8,
+ target, target_capacity,
+ &(*begin), source_size,
+ &error_code
+ );
+ if(U_FAILURE(error_code))
+ {
+ delete[] target;
+ throw Exception(
+ "Could not convert '"+encoding+"': "+u_errorName(error_code));
+ }
+
+ ucnv_close(converter);
+
+ std::string const result(target, target_size);
+
+ delete[] target;
+
+ return result;
+}
+
enum class Group
{
Alphabetic,
@@ -330,4 +372,101 @@ std::string as_utf8(
return result;
}
+std::string as_specific_character_set(
+ std::string const & input, Value::Strings const & specific_character_set,
+ bool is_pn)
+{
+ // Control characters: line feed, carriage return, form feed and tabulation
+ // For Person Name, add the group splitters
+ std::string splitters = "\n\r\f\t";
+ if(is_pn)
+ {
+ splitters.append("^=");
+ }
+
+ std::string result;
+
+ Group group=Group::Alphabetic;
+
+ auto begin = input.begin();
+ while(begin != input.end())
+ {
+ // Active character set resets to default before any of the splitters
+ // cf. PS 3.5, 6.1.2.5.3
+ auto const end = std::find_first_of(
+ begin, input.end(), splitters.begin(), splitters.end());
+
+ std::string encoded;
+
+ if(specific_character_set.empty())
+ {
+ encoded = std::string(begin, end);
+ }
+ else if(is_pn && group != Group::Alphabetic && specific_character_set.size() > 1)
+ {
+ // Encode using specific_character_set[1], using escape sequences
+ auto const encoder = find_encoder(specific_character_set[1]);
+ auto const it = escape_sequences.find(specific_character_set[1]);
+ auto const specific_escape_sequences =
+ (it!=escape_sequences.end())?it->second:std::vector<std::string>();
+
+ // The ISO-2022-JP encoder of ICU already includes the escape sequences
+ if(encoder != "ISO-2022-JP" && specific_escape_sequences.size() > 0)
+ {
+ encoded += specific_escape_sequences[0];
+ }
+ encoded += as_specific_character_set(begin, end, encoder);
+ if(encoder != "ISO-2022-JP" && specific_escape_sequences.size() > 1)
+ {
+ encoded += specific_escape_sequences[1];
+ }
+
+ if(
+ specific_character_set[0] == "ISO 2022 IR 13"
+ && encoder == "ISO-2022-JP")
+ {
+ // ICU switches back to ASCII (1B 28 42), while the examples
+ // from D. Clunie switch back to JIS X 0201-1976 (Romaji)
+ // (1B 28 4A)
+ encoded[encoded.size()-1] = 0x4a;
+ }
+ }
+ else
+ {
+ auto const encoder = find_encoder(specific_character_set[0]);
+ encoded = as_specific_character_set(begin, end, encoder);
+ }
+
+ result.append(encoded);
+
+ // If present, add the splitter to the UTF-8 string.
+ if(end != input.end())
+ {
+ result.push_back(*end);
+ begin = end+1;
+ if(is_pn && *end == '=')
+ {
+ if(group == Group::Alphabetic)
+ {
+ group = Group::Ideographic;
+ }
+ else if(group == Group::Ideographic)
+ {
+ group = Group::Phonetic;
+ }
+ else
+ {
+ throw Exception("Too many groups");
+ }
+ }
+ }
+ else
+ {
+ begin = end;
+ }
+ }
+
+ return result;
+}
+
}
diff --git a/src/odil/unicode.h b/src/odil/unicode.h
index 2628fc3..03a6f94 100644
--- a/src/odil/unicode.h
+++ b/src/odil/unicode.h
@@ -20,6 +20,11 @@ std::string as_utf8(
std::string const & input, Value::Strings const & specific_character_set,
bool is_pn=false);
+/// @brief Convert an UTF-8 string to a specific representation
+std::string as_specific_character_set(
+ std::string const & input, Value::Strings const & specific_character_set,
+ bool is_pn=false);
+
}
#endif // _4a178325_e3d6_4f6f_9a18_ba6a983ee396
diff --git a/tests/code/AssociationParameters.cpp b/tests/code/AssociationParameters.cpp
index e76540e..d2ffd2b 100644
--- a/tests/code/AssociationParameters.cpp
+++ b/tests/code/AssociationParameters.cpp
@@ -86,6 +86,16 @@ BOOST_AUTO_TEST_CASE(PresentationContexts)
BOOST_REQUIRE(parameters.get_presentation_contexts()[1].scp_role_support);
}
+BOOST_AUTO_TEST_CASE(UserIdentityDefault)
+{
+ odil::AssociationParameters parameters;
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::None);
+ BOOST_REQUIRE(parameters.get_user_identity().primary_field.empty());
+ BOOST_REQUIRE(parameters.get_user_identity().secondary_field.empty());
+}
+
BOOST_AUTO_TEST_CASE(UserIdentityNone)
{
odil::AssociationParameters parameters;
diff --git a/tests/code/Reader.cpp b/tests/code/Reader.cpp
index 13a0106..78395bd 100644
--- a/tests/code/Reader.cpp
+++ b/tests/code/Reader.cpp
@@ -15,6 +15,8 @@
#include "odil/VR.h"
#include "odil/dcmtk/conversion.h"
+#include "odil/json_converter.h"
+
BOOST_AUTO_TEST_CASE(Constructor)
{
std::istringstream stream;
@@ -77,100 +79,153 @@ void do_test(odil::DataSet const & odil_data_set)
}
}
-BOOST_AUTO_TEST_CASE(AT)
+template<typename T>
+void do_test(odil::Tag const & tag, odil::VR vr, std::initializer_list<T> const & value)
{
- odil::Element odil_element({"12345678", "9abcdef0"}, odil::VR::AT);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorATValue, odil_element);
+ // Empty element
+ {
+ odil::Element element(odil::Value(), vr);
+ odil::DataSet data_set;
+ data_set.add(tag, element);
+ do_test(data_set);
+ }
+ // Single value
+ {
+ odil::Element element({ *value.begin() }, vr);
+ odil::DataSet data_set;
+ data_set.add(tag, element);
+ do_test(data_set);
+ }
+ // Multiple values
+ {
+ odil::Element element(value, vr);
+ odil::DataSet data_set;
+ data_set.add(tag, element);
+ do_test(data_set);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(AE)
+{
+ do_test(
+ odil::registry::SelectorAEValue, odil::VR::AE,
+ {std::string("LOCAL"), std::string("REMOTE")});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(AS)
+{
+ do_test(
+ odil::registry::SelectorASValue, odil::VR::AS,
+ {std::string("035Y"), std::string("022W")});
+}
+
+BOOST_AUTO_TEST_CASE(AT)
+{
+ do_test(
+ odil::registry::SelectorATValue, odil::VR::AT,
+ {std::string("12345678"), std::string("9abcdef0")});
}
BOOST_AUTO_TEST_CASE(CS)
{
- odil::Element odil_element({"ABC", "DEF"}, odil::VR::CS);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorCSValue, odil_element);
+ do_test(
+ odil::registry::SelectorCSValue, odil::VR::CS,
+ {std::string("ABC"), std::string("DEF")});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(DA)
+{
+ do_test(
+ odil::registry::SelectorDAValue, odil::VR::DA,
+ {std::string("20160103"), std::string("19700131")});
}
BOOST_AUTO_TEST_CASE(DS)
{
- odil::Element odil_element({1.23, -4.56}, odil::VR::DS);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorDSValue, odil_element);
+ do_test(odil::registry::SelectorDSValue, odil::VR::DS, {1.23, -4.56});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(DT)
+{
+ do_test(
+ odil::registry::SelectorDTValue, odil::VR::DT,
+ {std::string("20160103112233"), std::string("19700131001122.123456")});
}
BOOST_AUTO_TEST_CASE(FD)
{
- odil::Element odil_element({1.23, -4.56}, odil::VR::FD);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorFDValue, odil_element);
-
- do_test(odil_data_set);
+ do_test(odil::registry::SelectorFDValue, odil::VR::FD, {1.23, -4.56});
}
BOOST_AUTO_TEST_CASE(FL)
{
- odil::Element odil_element({0.5, -0.125}, odil::VR::FL);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorFLValue, odil_element);
-
- do_test(odil_data_set);
+ do_test(odil::registry::SelectorFLValue, odil::VR::FL, {0.5, -0.125});
}
BOOST_AUTO_TEST_CASE(IS)
{
- odil::Element odil_element({123, -456}, odil::VR::IS);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorISValue, odil_element);
+ do_test(odil::registry::SelectorISValue, odil::VR::IS, {123, -456});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(LO)
+{
+ do_test(
+ odil::registry::SelectorLOValue, odil::VR::LO,
+ {std::string("Foo"), std::string("Bar")});
}
-BOOST_AUTO_TEST_CASE(OB)
+BOOST_AUTO_TEST_CASE(LT)
{
- odil::Element odil_element(
- odil::Value::Binary({{0x01, 0x02, 0x03, 0x04}}),
- odil::VR::OB);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::EncapsulatedDocument, odil_element);
+ do_test(
+ odil::registry::SelectorLTValue, odil::VR::LT,
+ {std::string("Foo\\Bar")});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(OB)
+{
+ do_test(
+ odil::registry::EncapsulatedDocument, odil::VR::OB, {
+ odil::Value::Binary::value_type{0x01, 0x02, 0x03, 0x04} });
}
+// OD is not in current DCMTK
+
BOOST_AUTO_TEST_CASE(OF)
{
- odil::Element odil_element(
- odil::Value::Binary({{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}),
- odil::VR::OF);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::VectorGridData, odil_element);
-
- do_test(odil_data_set);
+ do_test(
+ odil::registry::VectorGridData, odil::VR::OF, {
+ odil::Value::Binary::value_type{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08} });
}
+// OL is not in current DCMTK
+
BOOST_AUTO_TEST_CASE(OW)
{
- odil::Element odil_element(
- odil::Value::Binary({{0x01, 0x02, 0x03, 0x04}}),
- odil::VR::OW);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::RedPaletteColorLookupTableData, odil_element);
+ do_test(
+ odil::registry::RedPaletteColorLookupTableData, odil::VR::OW, {
+ odil::Value::Binary::value_type{0x01, 0x02, 0x03, 0x04} });
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(PN)
+{
+ do_test(
+ odil::registry::SelectorPNValue, odil::VR::PN, {
+ std::string("Adams^John Robert Quincy^^Rev.^B.A. M.Div."),
+ std::string("Morrison-Jones^Susan^^^Ph.D., Chief Executive Officer")
+ });
}
-BOOST_AUTO_TEST_CASE(SL)
+BOOST_AUTO_TEST_CASE(SH)
{
- odil::Element odil_element({12345678, -8765432}, odil::VR::SL);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorSLValue, odil_element);
+ do_test(
+ odil::registry::SelectorSHValue, odil::VR::SH,
+ {std::string("Foo"), std::string("Bar")});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(SL)
+{
+ do_test(odil::registry::SelectorSLValue, odil::VR::SL, {12345678, -8765432});
}
BOOST_AUTO_TEST_CASE(SQ)
@@ -184,48 +239,65 @@ BOOST_AUTO_TEST_CASE(SQ)
odil::registry::SelectorFDValue,
odil::Element({1.23, -4.56}, odil::VR::FD));
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::FrameExtractionSequence,
- odil::Element({item1, item2}, odil::VR::SQ));
-
- do_test(odil_data_set);
+ do_test(odil::registry::FrameExtractionSequence, odil::VR::SQ, {item1, item2});
}
BOOST_AUTO_TEST_CASE(SS)
{
- odil::Element odil_element({1234, -5678}, odil::VR::SS);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorSSValue, odil_element);
+ do_test(odil::registry::SelectorSSValue, odil::VR::SS, {1234, -5678});
+}
+
+BOOST_AUTO_TEST_CASE(ST)
+{
+ do_test(
+ odil::registry::SelectorSTValue, odil::VR::ST,
+ {std::string("Foo\\Bar")});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(TM)
+{
+ do_test(
+ odil::registry::SelectorTMValue, odil::VR::TM,
+ {std::string("112233"), std::string("001122.123456")});
}
+// UC is not in current DCMTK
+//{
+// do_test(
+// odil::registry::SelectorUCValue, odil::VR::UC,
+// {std::string("Foo"), std::string("Bar")});
+//}
+
BOOST_AUTO_TEST_CASE(UI)
{
- odil::Element odil_element({"1.2", "3.4"}, odil::VR::UI);
- odil::DataSet odil_data_set;
// SelectorUIValue is not in current DCMTK
- odil_data_set.add(odil::registry::SOPInstanceUID, odil_element);
-
- do_test(odil_data_set);
+ do_test(
+ odil::registry::SOPInstanceUID, odil::VR::UI,
+ {std::string("1.2"), std::string("3.4")});
}
BOOST_AUTO_TEST_CASE(UL)
{
- odil::Element odil_element({12345678, 8765432}, odil::VR::UL);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorULValue, odil_element);
-
- do_test(odil_data_set);
+ do_test(odil::registry::SelectorULValue, odil::VR::UL, {12345678, 8765432});
}
+// UR is not in current DCMTK
+//{
+// do_test(
+// odil::registry::SelectorURValue, odil::VR::UR,
+// {std::string("https://example.com"), std::string("mailto:me at example.com")});
+//}
+
BOOST_AUTO_TEST_CASE(US)
{
- odil::Element odil_element({1234, 5678}, odil::VR::US);
- odil::DataSet odil_data_set;
- odil_data_set.add(odil::registry::SelectorUSValue, odil_element);
+ do_test(odil::registry::SelectorUSValue, odil::VR::US, {1234, 5678});
+}
- do_test(odil_data_set);
+BOOST_AUTO_TEST_CASE(UT)
+{
+ do_test(
+ odil::registry::SelectorUTValue, odil::VR::UT,
+ {std::string("Foo\\Bar")});
}
void do_file_test(
diff --git a/tests/code/json_converter.cpp b/tests/code/json_converter.cpp
index 9cf7d3b..419bc9b 100644
--- a/tests/code/json_converter.cpp
+++ b/tests/code/json_converter.cpp
@@ -10,6 +10,7 @@
#include "odil/DataSet.h"
#include "odil/Element.h"
#include "odil/json_converter.h"
+#include "odil/registry.h"
#include "odil/Value.h"
#include "odil/VR.h"
@@ -100,6 +101,25 @@ BOOST_AUTO_TEST_CASE(AsJSONStrings)
&Json::Value::isString, &Json::Value::asString, data_set.as_string(0xdeadbeef));
}
+BOOST_AUTO_TEST_CASE(AsJSONStringsUnicode)
+{
+ odil::DataSet data_set;
+ data_set.add(
+ odil::registry::SpecificCharacterSet,
+ odil::Value::Strings({"ISO_IR 100"}), odil::VR::LO);
+ data_set.add(
+ 0xdeadbeef, odil::Value::Strings({"J\xe9r\xf4me"}), odil::VR::LO);
+ auto const json = odil::as_json(data_set);
+
+ check_json_object(
+ json, {std::string(odil::registry::SpecificCharacterSet), "deadbeef"});
+ check_json_object(json["deadbeef"], {"vr", "Value"});
+ check_json_string(json["deadbeef"]["vr"], "LO");
+ check_json_array(json["deadbeef"]["Value"],
+ &Json::Value::isString, &Json::Value::asString,
+ odil::Value::Strings{std::string("J\xc3\xa9r\xc3\xb4me")});
+}
+
BOOST_AUTO_TEST_CASE(AsJSONPersonName)
{
odil::DataSet data_set;
@@ -121,6 +141,28 @@ BOOST_AUTO_TEST_CASE(AsJSONPersonName)
check_json_string(json["deadbeef"]["Value"][0]["Phonetic"], {"Pho^Netic"});
}
+BOOST_AUTO_TEST_CASE(AsJSONPersonNameUnicode)
+{
+ odil::DataSet data_set;
+ data_set.add(
+ odil::registry::SpecificCharacterSet,
+ odil::Value::Strings({"ISO_IR 100"}), odil::VR::LO);
+ data_set.add(
+ 0xdeadbeef, odil::Value::Strings({"Buc^J\xe9r\xf4me"}), odil::VR::PN);
+ auto const json = odil::as_json(data_set);
+ check_json_object(
+ json, {std::string(odil::registry::SpecificCharacterSet), "deadbeef"});
+ check_json_object(json["deadbeef"], {"vr", "Value"});
+ check_json_string(json["deadbeef"]["vr"], "PN");
+
+ BOOST_REQUIRE(json["deadbeef"]["Value"].isArray());
+ BOOST_REQUIRE_EQUAL(json["deadbeef"]["Value"].size(), 1);
+ check_json_object(json["deadbeef"]["Value"][0], {"Alphabetic"});
+ check_json_string(
+ json["deadbeef"]["Value"][0]["Alphabetic"],
+ {"Buc^J\xc3\xa9r\xc3\xb4me"});
+}
+
BOOST_AUTO_TEST_CASE(AsJSONDataSets)
{
odil::DataSet item;
@@ -164,6 +206,15 @@ BOOST_AUTO_TEST_CASE(AsJSONBinary)
check_json_string(json["deadbeef"]["InlineBinary"], "AQIDBAU=");
}
+BOOST_AUTO_TEST_CASE(AsJSONGroupLength)
+{
+ odil::DataSet data_set;
+ data_set.add(0x00100000, {1234}, odil::VR::UL);
+ data_set.add(odil::registry::PatientID, {"DJ0001"});
+ auto const json = odil::as_json(data_set);
+ check_json_object(json, {"00100020"});
+}
+
BOOST_AUTO_TEST_CASE(AsDataSetEmpty)
{
std::stringstream data;
diff --git a/tests/code/unicode.cpp b/tests/code/unicode.cpp
index 8f7d16c..9d5d73f 100644
--- a/tests/code/unicode.cpp
+++ b/tests/code/unicode.cpp
@@ -4,7 +4,7 @@
#include "odil/DataSet.h"
#include "odil/unicode.h"
-BOOST_AUTO_TEST_CASE(SCSARAB)
+BOOST_AUTO_TEST_CASE(SCSARAB_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 127" };
std::string const source =
@@ -19,7 +19,22 @@ BOOST_AUTO_TEST_CASE(SCSARAB)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSFREN)
+BOOST_AUTO_TEST_CASE(SCSARAB_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 127" };
+ std::string const source =
+ "\xd9\x82\xd8\xa8\xd8\xa7\xd9\x86\xd9\x8a"
+ "^"
+ "\xd9\x84\xd9\x86\xd8\xb2\xd8\xa7\xd8\xb1";
+ std::string const expected =
+ "\xe2\xc8\xc7\xe6\xea" "^" "\xe4\xe6\xd2\xc7\xd1";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSFREN_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 100" };
std::string const source = "Buc" "^" "J\xe9r\xf4me";
@@ -31,7 +46,18 @@ BOOST_AUTO_TEST_CASE(SCSFREN)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSGERM)
+BOOST_AUTO_TEST_CASE(SCSFREN_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 100" };
+ std::string const source = "Buc" "^" "J\xc3\xa9r\xc3\xb4me";
+ std::string const expected = "Buc" "^" "J\xe9r\xf4me";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSGERM_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 100" };
std::string const source =
@@ -44,7 +70,18 @@ BOOST_AUTO_TEST_CASE(SCSGERM)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSGREEK)
+BOOST_AUTO_TEST_CASE(SCSGERM_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 100" };
+ std::string const source = "\xc3\x84neas" "^" "R\xc3\xbc" "diger";
+ std::string const expected = "\xc4neas" "^" "R\xfc" "diger";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSGREEK_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 126" };
std::string const source = "\xc4\xe9\xef\xed\xf5\xf3\xe9\xef\xf2";
@@ -56,7 +93,19 @@ BOOST_AUTO_TEST_CASE(SCSGREEK)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSH31)
+BOOST_AUTO_TEST_CASE(SCSGREEK_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 126" };
+ std::string const source =
+ "\xce\x94\xce\xb9\xce\xbf\xce\xbd\xcf\x85\xcf\x83\xce\xb9\xce\xbf\xcf\x82";
+ std::string const expected = "\xc4\xe9\xef\xed\xf5\xf3\xe9\xef\xf2";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSH31_AsUTF8)
{
odil::Value::Strings const specific_character_set =
{ "", "ISO 2022 IR 87" };
@@ -81,7 +130,32 @@ BOOST_AUTO_TEST_CASE(SCSH31)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSH32)
+BOOST_AUTO_TEST_CASE(SCSH31_AsSCS)
+{
+ odil::Value::Strings const specific_character_set =
+ { "", "ISO 2022 IR 87" };
+ std::string const source =
+ "Yamada" "^" "Tarou"
+ "="
+ "\xe5\xb1\xb1\xe7\x94\xb0" "^" "\xe5\xa4\xaa\xe9\x83\x8e"
+ "="
+ "\xe3\x82\x84\xe3\x81\xbe\xe3\x81\xa0"
+ "^" "\xe3\x81\x9f\xe3\x82\x8d\xe3\x81\x86";
+ std::string const expected =
+ "Yamada" "^" "Tarou"
+ "="
+ "\x1b\x24\x42\x3b\x33\x45\x44\x1b\x28\x42"
+ "^" "\x1b\x24\x42\x42\x40\x4f\x3a\x1b\x28\x42"
+ "="
+ "\x1b\x24\x42\x24\x64\x24\x5e\x24\x40\x1b\x28\x42"
+ "^" "\x1b\x24\x42\x24\x3f\x24\x6d\x24\x26\x1b\x28\x42";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSH32_AsUTF8)
{
odil::Value::Strings const specific_character_set =
{ "ISO 2022 IR 13", "ISO 2022 IR 87" };
@@ -108,7 +182,34 @@ BOOST_AUTO_TEST_CASE(SCSH32)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSHBRW)
+BOOST_AUTO_TEST_CASE(SCSH32_AsSCS)
+{
+ odil::Value::Strings const specific_character_set =
+ { "ISO 2022 IR 13", "ISO 2022 IR 87" };
+ std::string const source =
+ "\xef\xbe\x94\xef\xbe\x8f\xef\xbe\x80\xef\xbe\x9e"
+ "^" "\xef\xbe\x80\xef\xbe\x9b\xef\xbd\xb3"
+ "="
+ "\xe5\xb1\xb1\xe7\x94\xb0"
+ "^" "\xe5\xa4\xaa\xe9\x83\x8e"
+ "="
+ "\xe3\x82\x84\xe3\x81\xbe\xe3\x81\xa0"
+ "^" "\xe3\x81\x9f\xe3\x82\x8d\xe3\x81\x86";
+ std::string const expected =
+ "\xd4\xcf\xc0\xde" "^" "\xc0\xdb\xb3"
+ "="
+ "\x1b\x24\x42\x3b\x33\x45\x44\x1b\x28\x4a"
+ "^" "\x1b\x24\x42\x42\x40\x4f\x3a\x1b\x28\x4a"
+ "="
+ "\x1b\x24\x42\x24\x64\x24\x5e\x24\x40\x1b\x28\x4a"
+ "^" "\x1b\x24\x42\x24\x3f\x24\x6d\x24\x26\x1b\x28\x4a";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSHBRW_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 138" };
std::string const source = "\xf9\xf8\xe5\xef" "^" "\xe3\xe1\xe5\xf8\xe4";
@@ -121,7 +222,20 @@ BOOST_AUTO_TEST_CASE(SCSHBRW)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSI2)
+BOOST_AUTO_TEST_CASE(SCSHBRW_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 138" };
+ std::string const source =
+ "\xd7\xa9\xd7\xa8\xd7\x95\xd7\x9f"
+ "^" "\xd7\x93\xd7\x91\xd7\x95\xd7\xa8\xd7\x94";
+ std::string const expected = "\xf9\xf8\xe5\xef" "^" "\xe3\xe1\xe5\xf8\xe4";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSI2_AsUTF8)
{
odil::Value::Strings const specific_character_set =
{ "", "ISO 2022 IR 149" };
@@ -144,7 +258,29 @@ BOOST_AUTO_TEST_CASE(SCSI2)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSRUSS)
+BOOST_AUTO_TEST_CASE(SCSI2_AsSCS)
+{
+ odil::Value::Strings const specific_character_set =
+ { "", "ISO 2022 IR 149" };
+ std::string const source =
+ "Hong" "^" "Gildong"
+ "="
+ "\xe6\xb4\xaa" "^" "\xe5\x90\x89\xe6\xb4\x9e"
+ "="
+ "\xed\x99\x8d" "^" "\xea\xb8\xb8\xeb\x8f\x99";
+ std::string const expected =
+ "Hong" "^" "Gildong"
+ "="
+ "\x1b\x24\x29\x43\xfb\xf3" "^" "\x1b\x24\x29\x43\xd1\xce\xd4\xd7"
+ "="
+ "\x1b\x24\x29\x43\xc8\xab" "^" "\x1b\x24\x29\x43\xb1\xe6\xb5\xbf";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSRUSS_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 144" };
std::string const source = "\xbb\xee\xda\x63\x65\xdc\xd1\x79\x70\xd3";
@@ -156,7 +292,19 @@ BOOST_AUTO_TEST_CASE(SCSRUSS)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSX1)
+BOOST_AUTO_TEST_CASE(SCSRUSS_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 144" };
+ std::string const source =
+ "\xd0\x9b\xd1\x8e\xd0\xba\x63\x65\xd0\xbc\xd0\xb1\x79\x70\xd0\xb3";
+ std::string const expected = "\xbb\xee\xda\x63\x65\xdc\xd1\x79\x70\xd3";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSX1_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "ISO_IR 192" };
std::string const source =
@@ -166,7 +314,8 @@ BOOST_AUTO_TEST_CASE(SCSX1)
"=";
std::string const expected =
"Wang" "^" "XiaoDong"
- "\x3d\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe6\x9d\xb1"
+ "="
+ "\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe6\x9d\xb1"
"=";
std::string const utf8 = odil::as_utf8(
@@ -174,7 +323,26 @@ BOOST_AUTO_TEST_CASE(SCSX1)
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-BOOST_AUTO_TEST_CASE(SCSX2)
+BOOST_AUTO_TEST_CASE(SCSX1_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "ISO_IR 192" };
+ std::string const source =
+ "Wang" "^" "XiaoDong"
+ "="
+ "\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe6\x9d\xb1"
+ "=";
+ std::string const expected =
+ "Wang" "^" "XiaoDong"
+ "="
+ "\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe6\x9d\xb1"
+ "=";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
+
+BOOST_AUTO_TEST_CASE(SCSX2_AsUTF8)
{
odil::Value::Strings const specific_character_set = { "GB18030" };
std::string const source =
@@ -192,3 +360,22 @@ BOOST_AUTO_TEST_CASE(SCSX2)
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
+
+BOOST_AUTO_TEST_CASE(SCSX2_AsSCS)
+{
+ odil::Value::Strings const specific_character_set = { "GB18030" };
+ std::string const source =
+ "Wang" "^" "XiaoDong"
+ "="
+ "\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe4\xb8\x9c"
+ "=";
+ std::string const expected =
+ "Wang" "^" "XiaoDong"
+ "=" ""
+ "\xcd\xf5" "^" "\xd0\xa1\xb6\xab"
+ "=";
+
+ std::string const scs = odil::as_specific_character_set(
+ source, specific_character_set, true);
+ BOOST_REQUIRE_EQUAL(scs, expected);
+}
diff --git a/tests/wrappers/test_association_parameters.py b/tests/wrappers/test_association_parameters.py
index fb4b8d0..721f77b 100644
--- a/tests/wrappers/test_association_parameters.py
+++ b/tests/wrappers/test_association_parameters.py
@@ -27,11 +27,8 @@ class TestAssociationParameters(unittest.TestCase):
self.assertEqual(parameters.get_calling_ae_title(), "foo")
def test_presentation_contexts(self):
- presentation_context = odil.AssociationParameters.PresentationContext()
- presentation_context.id = 1
- presentation_context.abstract_syntax = "foo"
- presentation_context.transfer_syntaxes.append("bar")
-
+ presentation_context = odil.AssociationParameters.PresentationContext(
+ 1, "foo", ["bar"], True, False)
parameters = odil.AssociationParameters()
parameters.set_presentation_contexts([presentation_context])
diff --git a/tests/wrappers/test_value.py b/tests/wrappers/test_value.py
index f4b2f79..b461690 100644
--- a/tests/wrappers/test_value.py
+++ b/tests/wrappers/test_value.py
@@ -90,5 +90,11 @@ class TestValueBinary(unittest.TestCase):
data = odil.Value.Binary(items)
self.assertEqual([x for x in data[0]], [x for x in items[0]])
+ def test_buffer(self):
+ item = odil.Value.BinaryItem("\x01\x02\x03")
+ memory_view = item.get_memory_view()
+ self.assertTrue(isinstance(memory_view, memoryview))
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/wrappers/AssocationParameters.cpp b/wrappers/AssocationParameters.cpp
index f545c5e..e799795 100644
--- a/wrappers/AssocationParameters.cpp
+++ b/wrappers/AssocationParameters.cpp
@@ -56,13 +56,14 @@ set_presentation_contexts(
boost::python::list const & presentation_contexts)
{
std::vector<odil::AssociationParameters::PresentationContext>
- presentation_contexts_cpp(boost::python::len(presentation_contexts));
+ presentation_contexts_cpp;
+ presentation_contexts_cpp.reserve(boost::python::len(presentation_contexts));
for(int i = 0; i<boost::python::len(presentation_contexts); ++i)
{
- presentation_contexts_cpp[i] =
+ presentation_contexts_cpp.push_back(
boost::python::extract<
odil::AssociationParameters::PresentationContext
- >(presentation_contexts[i]);
+ >(presentation_contexts[i]));
}
parameters.set_presentation_contexts(presentation_contexts_cpp);
@@ -150,7 +151,8 @@ void wrap_AssociationParameters()
{
scope presentation_context_scope =
- class_<AssociationParameters::PresentationContext>("PresentationContext")
+ class_<AssociationParameters::PresentationContext>(
+ "PresentationContext", no_init)
.def(
"__init__",
make_constructor(&presentation_context_constructor))
diff --git a/wrappers/Value.cpp b/wrappers/Value.cpp
index 92a7751..829b13e 100644
--- a/wrappers/Value.cpp
+++ b/wrappers/Value.cpp
@@ -35,6 +35,19 @@ boost::shared_ptr<T> create_value(boost::python::object const & sequence)
return boost::shared_ptr<T>(new T(values));
}
+boost::python::object
+as_memory_view(odil::Value::Binary::value_type const & binary_item)
+{
+ Py_buffer buffer;
+ PyBuffer_FillInfo(
+ &buffer, nullptr,
+ const_cast<odil::Value::Binary::value_type::value_type*>(&binary_item[0]),
+ binary_item.size(), 1, PyBUF_SIMPLE);
+ PyObject * memory_view = PyMemoryView_FromBuffer(&buffer);
+
+ return boost::python::object(boost::python::handle<>(memory_view));
+}
+
}
void wrap_Value()
@@ -111,6 +124,7 @@ void wrap_Value()
"__init__",
make_constructor(create_value<Value::Binary::value_type, char>))
.def(vector_indexing_suite<Value::Binary::value_type>())
+ .def("get_memory_view", as_memory_view)
;
class_<Value::Binary>("Binary")
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/odil.git
More information about the debian-med-commit
mailing list