blob: 735269984705dfe9c5098eeea14d1fa8a2d630ff [file] [log] [blame]
Mickey Sweatt3b0bea62016-01-25 22:12:27 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3* Copyright (c) 2016 Regents of the University of California.
4*
5* This file is part of the nTorrent codebase.
6*
7* nTorrent is free software: you can redistribute it and/or modify it under the
8* terms of the GNU Lesser General Public License as published by the Free Software
9* Foundation, either version 3 of the License, or (at your option) any later version.
10*
11* nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY
12* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14*
15* You should have received copies of the GNU General Public License and GNU Lesser
16* General Public License along with nTorrent, e.g., in COPYING.md file. If not, see
17* <http://www.gnu.org/licenses/>.
18*
19* See AUTHORS for complete list of nTorrent authors and contributors.
20*/
21#include "file-manifest.hpp"
22
23#include <boost/assert.hpp>
24#include <boost/range/adaptors.hpp>
25#include <boost/throw_exception.hpp>
26
27using std::vector;
28
29namespace ndn {
30namespace ntorrent {
31
32BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FileManifest>));
33BOOST_CONCEPT_ASSERT((WireEncodable<FileManifest>));
34BOOST_CONCEPT_ASSERT((WireDecodable<FileManifest>));
35static_assert(std::is_base_of<Data::Error, FileManifest::Error>::value,
36 "FileManifest::Error should inherit from Data::Error");
37
38void
39FileManifest::wireDecode(const Block& wire)
40{
41 Data::wireDecode(wire);
42 this->decodeContent();
43}
44
45const Block&
46FileManifest::wireEncode()
47{
48 encodeContent();
49 return Data::wireEncode();
50}
51
52template<ndn::encoding::Tag TAG>
53size_t
54FileManifest::encodeContent(ndn::EncodingImpl<TAG>& encoder) const {
55 // FileManifestName ::= NAME-TYPE TLV-LENGTH
56 // Name
57
58 // ManifestContent ::= CONTENT-TYPE TLV-LENGTH
59 // DataPacketName*
60 // CatalogPrefix
61 // DataPacketSize
62 // FileManifestPtr?
63
64 // DataPacketName ::= NAME-TYPE TLV-LENGTH
65 // Name
66
67 // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
68 // Name
69
70 // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
71 // nonNegativeInteger
72
73 // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
74 // Name
75
76 size_t totalLength = 0;
77
78 vector<Name> suffixCatalog;
79 suffixCatalog.reserve(m_catalog.size());
80 for (auto name: m_catalog) {
81 if (!m_catalogPrefix.isPrefixOf(name)) {
82 BOOST_THROW_EXCEPTION(Error(name.toUri() + "does not have the prefix"
83 + m_catalogPrefix.toUri()));
84 }
85 name = name.getSubName(m_catalogPrefix.size());
86 if (name.empty()) {
87 BOOST_THROW_EXCEPTION(Error("Manifest cannot include empty string"));
88 }
89 suffixCatalog.push_back(name);
90 }
91
92 for (const auto& name : suffixCatalog | boost::adaptors::reversed) {
93 totalLength += name.wireEncode(encoder);
94 }
95
96 totalLength += m_catalogPrefix.wireEncode(encoder);
97
98 totalLength += prependNonNegativeIntegerBlock(encoder, tlv::Content, m_dataPacketSize);
99
100 if (nullptr != m_submanifestPtr) {
101 totalLength += m_submanifestPtr->wireEncode(encoder);
102 }
103
104 totalLength += encoder.prependVarNumber(totalLength);
105 totalLength += encoder.prependVarNumber(tlv::Content);
106 return totalLength;
107
108}
109
110// MANIPULATORS
111void
112FileManifest::push_back(const Name& name)
113{
114 BOOST_ASSERT(name != m_catalogPrefix);
115 BOOST_ASSERT(m_catalogPrefix.isPrefixOf(name));
116 m_catalog.push_back(name);
117}
118
119bool
120FileManifest::remove(const ndn::Name& name) {
121 auto it = std::find(m_catalog.begin(), m_catalog.end(), name);
122 if (m_catalog.end() == it) {
123 return false;
124 }
125 m_catalog.erase(it);
126 return true;
127}
128
129void FileManifest::encodeContent() {
130 // Name
131 // <file_name>/ImplicitDigest
132 // Content
133 // MetaData
134 // DataPtr*
135 // ManifestPointer?
136 //
137 // DataPtr := HashValue
138 // ManifestPtr := HashValue
139 // HashValue := OCTET[32]
140
141 // MetaData := Property*
142 // Property := DataSize | Signature
143 onChanged();
144
145 EncodingEstimator estimator;
146 size_t estimatedSize = encodeContent(estimator);
147
148 EncodingBuffer buffer(estimatedSize, 0);
149 encodeContent(buffer);
150
151 setContentType(tlv::ContentType_Blob);
152 setContent(buffer.block());
153}
154
155void
156FileManifest::decodeContent() {
157 // ManifestContent ::= CONTENT-TYPE TLV-LENGTH
158 // DataPacketName*
159 // CatalogPrefix
160 // DataPacketSize
161 // FileManifestPtr?
162
163
164 // DataPacketName ::= NAME-TYPE TLV-LENGTH
165 // Name
166
167 // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
168 // Name
169
170 // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
171 // nonNegativeInteger
172
173 // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
174 // Name
175
176 if (getContentType() != tlv::ContentType_Blob) {
177 BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob"));
178 }
179
180 const Block& content = Data::getContent();
181 content.parse();
182
183 auto element = content.elements_begin();
184
185 if (content.elements_end() == element) {
186 BOOST_THROW_EXCEPTION(Error("FileManifest with empty content"));
187 }
188
189 element->parse();
190 Name name(*element);
191
192 // Submanifest ptr
193 if (!name.empty()) {
194 m_submanifestPtr = std::make_shared<Name>(name);
195 ++element;
196 }
197
198 // DataPacketSize
199 m_dataPacketSize = readNonNegativeInteger(*element);
200 ++element;
201
202 // CatalogPrefix
203 m_catalogPrefix = Name(*element);
204 element++;
205
206 // Catalog
207 m_catalog.clear();
208 for (; element != content.elements_end(); ++element) {
209 element->parse();
210 name = m_catalogPrefix;
211 name.append(Name(*element));
212 if (name == m_catalogPrefix) {
213 BOOST_THROW_EXCEPTION(Error("Empty name included in a FileManifest"));
214 }
215 push_back(name);
216 }
217}
218
219bool operator==(const FileManifest& lhs, const FileManifest& rhs) {
220 return lhs.name() == rhs.name()
221 && lhs.data_packet_size() == rhs.data_packet_size()
222 && (lhs.submanifest_ptr() == rhs.submanifest_ptr() /* shallow equality */
223 || ( nullptr != lhs.submanifest_ptr()
224 && nullptr != rhs.submanifest_ptr()
225 && *rhs.submanifest_ptr() == *lhs.submanifest_ptr()
226 )
227 )
228 && lhs.catalog() == rhs.catalog();
229}
230
231bool operator!=(const FileManifest& lhs, const FileManifest& rhs) {
232 return lhs.name() != rhs.name()
233 || lhs.data_packet_size() != rhs.data_packet_size()
234 || (lhs.submanifest_ptr() != rhs.submanifest_ptr() /* shallow equality */
235 && (nullptr == lhs.submanifest_ptr()
236 || nullptr == rhs.submanifest_ptr()
237 || *rhs.submanifest_ptr() != *lhs.submanifest_ptr()
238 )
239 )
240 || lhs.catalog() != rhs.catalog();
241}
242
243} // end ntorrent
244} // end ndn