rdpf.tcc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Templated method implementations for rdpf.hpp
  2. // Create a StreamEval object that will start its output at index start.
  3. // It will wrap around to 0 when it hits 2^depth. If use_expansion
  4. // is true, then if the DPF has been expanded, just output values
  5. // from that. If use_expansion=false or if the DPF has not been
  6. // expanded, compute the values on the fly.
  7. template <typename T>
  8. StreamEval<T>::StreamEval(const T &rdpf, address_t start,
  9. size_t &op_counter, bool use_expansion) : rdpf(rdpf),
  10. op_counter(op_counter), use_expansion(use_expansion)
  11. {
  12. depth = rdpf.depth();
  13. // Prevent overflow of 1<<depth
  14. if (depth < ADDRESS_MAX_BITS) {
  15. indexmask = (address_t(1)<<depth)-1;
  16. } else {
  17. indexmask = ~0;
  18. }
  19. start &= indexmask;
  20. // Record that we haven't actually output the leaf for index start
  21. // itself yet
  22. nextindex = start;
  23. if (use_expansion && rdpf.has_expansion()) {
  24. // We just need to keep the counter, not compute anything
  25. return;
  26. }
  27. path.resize(depth);
  28. pathindex = start;
  29. path[0] = rdpf.get_seed();
  30. for (nbits_t i=1;i<depth;++i) {
  31. bool dir = !!(pathindex & (address_t(1)<<(depth-i)));
  32. path[i] = rdpf.descend(path[i-1], i-1, dir, op_counter);
  33. }
  34. }
  35. template <typename T>
  36. typename T::node StreamEval<T>::next()
  37. {
  38. if (use_expansion && rdpf.has_expansion()) {
  39. // Just use the precomputed values
  40. typename T::node leaf = rdpf.get_expansion(nextindex);
  41. nextindex = (nextindex + 1) & indexmask;
  42. return leaf;
  43. }
  44. // Invariant: in the first call to next(), nextindex = pathindex.
  45. // Otherwise, nextindex = pathindex+1.
  46. // Get the XOR of nextindex and pathindex, and strip the low bit.
  47. // If nextindex and pathindex are equal, or pathindex is even
  48. // and nextindex is the consecutive odd number, index_xor will be 0,
  49. // indicating that we don't have to update the path, but just
  50. // compute the appropriate leaf given by the low bit of nextindex.
  51. //
  52. // Otherwise, say for example pathindex is 010010111 and nextindex
  53. // is 010011000. Then their XOR is 000001111, and stripping the low
  54. // bit yields 000001110, so how_many_1_bits will be 3.
  55. // That indicates (typically) that path[depth-3] was a left child,
  56. // and now we need to change it to a right child by descending right
  57. // from path[depth-4], and then filling the path after that with
  58. // left children.
  59. //
  60. // When we wrap around, however, index_xor will be 111111110 (after
  61. // we strip the low bit), and how_many_1_bits will be depth-1, but
  62. // the new top child (of the root seed) we have to compute will be a
  63. // left, not a right, child.
  64. uint64_t index_xor = (nextindex ^ pathindex) & ~1;
  65. nbits_t how_many_1_bits = __builtin_popcount(index_xor);
  66. if (how_many_1_bits > 0) {
  67. // This will almost always be 1, unless we've just wrapped
  68. // around from the right subtree back to the left, in which case
  69. // it will be 0.
  70. bool top_changed_bit =
  71. nextindex & (address_t(1) << how_many_1_bits);
  72. path[depth-how_many_1_bits] =
  73. rdpf.descend(path[depth-how_many_1_bits-1],
  74. depth-how_many_1_bits-1, top_changed_bit, op_counter);
  75. for (nbits_t i = depth-how_many_1_bits; i < depth-1; ++i) {
  76. path[i+1] = rdpf.descend(path[i], i, 0, op_counter);
  77. }
  78. }
  79. typename T::node leaf = rdpf.descend(path[depth-1], depth-1,
  80. nextindex & 1, op_counter);
  81. pathindex = nextindex;
  82. nextindex = (nextindex + 1) & indexmask;
  83. return leaf;
  84. }
  85. // Additive share of the scaling value M_as such that the high words
  86. // of the leaf values for P0 and P1 add to M_as * e_{target}
  87. template <>
  88. inline std::tuple<RegAS,RegAS,RegAS> RDPFTriple::scaled_value<RegAS>() const {
  89. return std::make_tuple(dpf[0].scaled_sum, dpf[1].scaled_sum,
  90. dpf[2].scaled_sum);
  91. }
  92. // XOR share of the scaling value M_xs such that the high words
  93. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  94. template <>
  95. inline std::tuple<RegXS,RegXS,RegXS> RDPFTriple::scaled_value<RegXS>() const {
  96. return std::make_tuple(dpf[0].scaled_xor, dpf[1].scaled_xor,
  97. dpf[2].scaled_xor);
  98. }
  99. // Get the bit-shared unit vector entry from the leaf node
  100. template <>
  101. inline std::tuple<RegXS,RegXS,RegXS> RDPFTriple::unit<RegXS>(node leaf) const {
  102. return std::make_tuple(
  103. dpf[0].unit_bs(std::get<0>(leaf)),
  104. dpf[1].unit_bs(std::get<1>(leaf)),
  105. dpf[2].unit_bs(std::get<2>(leaf)));
  106. }
  107. // Get the additive-shared unit vector entry from the leaf node
  108. template <>
  109. inline std::tuple<RegAS,RegAS,RegAS> RDPFTriple::unit<RegAS>(node leaf) const {
  110. return std::make_tuple(
  111. dpf[0].unit_as(std::get<0>(leaf)),
  112. dpf[1].unit_as(std::get<1>(leaf)),
  113. dpf[2].unit_as(std::get<2>(leaf)));
  114. }
  115. // Get the XOR-shared scaled vector entry from the leaf ndoe
  116. template <>
  117. inline std::tuple<RegXS,RegXS,RegXS> RDPFTriple::scaled<RegXS>(node leaf) const {
  118. return std::make_tuple(
  119. dpf[0].scaled_xs(std::get<0>(leaf)),
  120. dpf[1].scaled_xs(std::get<1>(leaf)),
  121. dpf[2].scaled_xs(std::get<2>(leaf)));
  122. }
  123. // Get the additive-shared scaled vector entry from the leaf node
  124. template <>
  125. inline std::tuple<RegAS,RegAS,RegAS> RDPFTriple::scaled<RegAS>(node leaf) const {
  126. return std::make_tuple(
  127. dpf[0].scaled_as(std::get<0>(leaf)),
  128. dpf[1].scaled_as(std::get<1>(leaf)),
  129. dpf[2].scaled_as(std::get<2>(leaf)));
  130. }
  131. // Additive share of the scaling value M_as such that the high words
  132. // of the leaf values for P0 and P1 add to M_as * e_{target}
  133. template <>
  134. inline std::tuple<RegAS,RegAS> RDPFPair::scaled_value<RegAS>() const {
  135. return std::make_tuple(dpf[0].scaled_sum, dpf[1].scaled_sum);
  136. }
  137. // XOR share of the scaling value M_xs such that the high words
  138. // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
  139. template <>
  140. inline std::tuple<RegXS,RegXS> RDPFPair::scaled_value<RegXS>() const {
  141. return std::make_tuple(dpf[0].scaled_xor, dpf[1].scaled_xor);
  142. }
  143. // Get the bit-shared unit vector entry from the leaf node
  144. template <>
  145. inline std::tuple<RegXS,RegXS> RDPFPair::unit<RegXS>(node leaf) const {
  146. return std::make_tuple(
  147. dpf[0].unit_bs(std::get<0>(leaf)),
  148. dpf[1].unit_bs(std::get<1>(leaf)));
  149. }
  150. // Get the additive-shared unit vector entry from the leaf node
  151. template <>
  152. inline std::tuple<RegAS,RegAS> RDPFPair::unit<RegAS>(node leaf) const {
  153. return std::make_tuple(
  154. dpf[0].unit_as(std::get<0>(leaf)),
  155. dpf[1].unit_as(std::get<1>(leaf)));
  156. }
  157. // Get the XOR-shared scaled vector entry from the leaf ndoe
  158. template <>
  159. inline std::tuple<RegXS,RegXS> RDPFPair::scaled<RegXS>(node leaf) const {
  160. return std::make_tuple(
  161. dpf[0].scaled_xs(std::get<0>(leaf)),
  162. dpf[1].scaled_xs(std::get<1>(leaf)));
  163. }
  164. // Get the additive-shared scaled vector entry from the leaf node
  165. template <>
  166. inline std::tuple<RegAS,RegAS> RDPFPair::scaled<RegAS>(node leaf) const {
  167. return std::make_tuple(
  168. dpf[0].scaled_as(std::get<0>(leaf)),
  169. dpf[1].scaled_as(std::get<1>(leaf)));
  170. }
  171. // I/O for RDPFs
  172. template <typename T>
  173. T& operator>>(T &is, RDPF &rdpf)
  174. {
  175. is.read((char *)&rdpf.seed, sizeof(rdpf.seed));
  176. uint8_t depth;
  177. // The whichhalf bit is the high bit of depth
  178. is.read((char *)&depth, sizeof(depth));
  179. rdpf.whichhalf = !!(depth & 0x80);
  180. depth &= 0x7f;
  181. bool read_expanded = false;
  182. if (depth > 64) {
  183. read_expanded = true;
  184. depth -= 64;
  185. }
  186. assert(depth <= ADDRESS_MAX_BITS);
  187. rdpf.cw.clear();
  188. for (uint8_t i=0; i<depth; ++i) {
  189. DPFnode cw;
  190. is.read((char *)&cw, sizeof(cw));
  191. rdpf.cw.push_back(cw);
  192. }
  193. if (read_expanded) {
  194. rdpf.expansion.resize(1<<depth);
  195. is.read((char *)rdpf.expansion.data(),
  196. sizeof(rdpf.expansion[0])<<depth);
  197. }
  198. value_t cfbits = 0;
  199. is.read((char *)&cfbits, BITBYTES(depth));
  200. rdpf.cfbits = cfbits;
  201. is.read((char *)&rdpf.unit_sum_inverse, sizeof(rdpf.unit_sum_inverse));
  202. is.read((char *)&rdpf.scaled_sum, sizeof(rdpf.scaled_sum));
  203. is.read((char *)&rdpf.scaled_xor, sizeof(rdpf.scaled_xor));
  204. return is;
  205. }
  206. // Write the DPF to the output stream. If expanded=true, then include
  207. // the expansion _if_ the DPF is itself already expanded. You can use
  208. // this to write DPFs to files.
  209. template <typename T>
  210. T& write_maybe_expanded(T &os, const RDPF &rdpf,
  211. bool expanded = true)
  212. {
  213. os.write((const char *)&rdpf.seed, sizeof(rdpf.seed));
  214. uint8_t depth = rdpf.cw.size();
  215. assert(depth <= ADDRESS_MAX_BITS);
  216. // The whichhalf bit is the high bit of depth
  217. // If we're writing an expansion, add 64 to depth as well
  218. uint8_t whichhalf_and_depth = depth |
  219. (uint8_t(rdpf.whichhalf)<<7);
  220. bool write_expansion = false;
  221. if (expanded && rdpf.expansion.size() == (size_t(1)<<depth)) {
  222. write_expansion = true;
  223. whichhalf_and_depth += 64;
  224. }
  225. os.write((const char *)&whichhalf_and_depth,
  226. sizeof(whichhalf_and_depth));
  227. for (uint8_t i=0; i<depth; ++i) {
  228. os.write((const char *)&rdpf.cw[i], sizeof(rdpf.cw[i]));
  229. }
  230. if (write_expansion) {
  231. os.write((const char *)rdpf.expansion.data(),
  232. sizeof(rdpf.expansion[0])<<depth);
  233. }
  234. os.write((const char *)&rdpf.cfbits, BITBYTES(depth));
  235. os.write((const char *)&rdpf.unit_sum_inverse, sizeof(rdpf.unit_sum_inverse));
  236. os.write((const char *)&rdpf.scaled_sum, sizeof(rdpf.scaled_sum));
  237. os.write((const char *)&rdpf.scaled_xor, sizeof(rdpf.scaled_xor));
  238. return os;
  239. }
  240. // The ordinary << version never writes the expansion, since this is
  241. // what we use to send DPFs over the network.
  242. template <typename T>
  243. T& operator<<(T &os, const RDPF &rdpf)
  244. {
  245. return write_maybe_expanded(os, rdpf, false);
  246. }
  247. // I/O for RDPF Triples
  248. // We never write RDPFTriples over the network, so always write
  249. // the DPF expansions if they're available.
  250. template <typename T>
  251. T& operator<<(T &os, const RDPFTriple &rdpftrip)
  252. {
  253. write_maybe_expanded(os, rdpftrip.dpf[0], true);
  254. write_maybe_expanded(os, rdpftrip.dpf[1], true);
  255. write_maybe_expanded(os, rdpftrip.dpf[2], true);
  256. nbits_t depth = rdpftrip.dpf[0].depth();
  257. os.write((const char *)&rdpftrip.as_target.ashare, BITBYTES(depth));
  258. os.write((const char *)&rdpftrip.xs_target.xshare, BITBYTES(depth));
  259. return os;
  260. }
  261. template <typename T>
  262. T& operator>>(T &is, RDPFTriple &rdpftrip)
  263. {
  264. is >> rdpftrip.dpf[0] >> rdpftrip.dpf[1] >> rdpftrip.dpf[2];
  265. nbits_t depth = rdpftrip.dpf[0].depth();
  266. rdpftrip.as_target.ashare = 0;
  267. is.read((char *)&rdpftrip.as_target.ashare, BITBYTES(depth));
  268. rdpftrip.xs_target.xshare = 0;
  269. is.read((char *)&rdpftrip.xs_target.xshare, BITBYTES(depth));
  270. return is;
  271. }
  272. // I/O for RDPF Pairs
  273. // We never write RDPFPairs over the network, so always write
  274. // the DPF expansions if they're available.
  275. template <typename T>
  276. T& operator<<(T &os, const RDPFPair &rdpfpair)
  277. {
  278. write_maybe_expanded(os, rdpfpair.dpf[0], true);
  279. write_maybe_expanded(os, rdpfpair.dpf[1], true);
  280. return os;
  281. }
  282. template <typename T>
  283. T& operator>>(T &is, RDPFPair &rdpfpair)
  284. {
  285. is >> rdpfpair.dpf[0] >> rdpfpair.dpf[1];
  286. return is;
  287. }