DeviceAtlas C++ Api documentation
devatlas_priv.h
1 #ifndef devatlas_priv_h
2 #define devatlas_priv_h
3 #include <mtld/devatlas.h>
4 #include <mtld/common_priv.h>
5 #include <limits>
6 
7 namespace Mobi { namespace Mtld { namespace Da {
8 
9 
10 struct LocalProperty {
11  Property property;
12  const Value *value;
13 };
14 
15 static const size_t LOCAL_PROP_COUNT=16;
17  size_t count;
18  LocalProperty properties[LOCAL_PROP_COUNT];
19  LocalProperties *next;
20 public:
21  LocalProperty *get(size_t);
22  LocalProperty *get(const char *);
23  LocalProperty *allocNew(DeliveryContext *ctx, const char *, PropertyType);
25  : count(0)
26  , next(0)
27  {
28  }
29  size_t size() const;
30 };
31 
32 class NodeData;
33 
34 /*
35  * a set of properties
36  * This is only used if the mask and/or conflicts processing is in use
37  */
38 class propset {
39  typedef unsigned int I;
40  I *vals;
41  static const int mask = sizeof (I) * 8 - 1;
42 
43  static unsigned idxOf(unsigned item) {
44  return item / sizeof(I);
45  }
46 
47 public:
48  propset(size_t maxcount = 200) {
49  size_t count = idxOf(maxcount | mask) + 1;
50  vals = new I[count];
51  memset(vals, 0, sizeof (I) * count);
52  }
53  ~propset() {
54  delete[] vals;
55  }
56 
57  bool find(size_t v) const {
58  return vals[idxOf(v)] & (1 << (v & mask));
59  }
60 
61  bool end() const {
62  return false;
63  }
64 
65  void insert(unsigned v) {
66  vals[idxOf(v)] |= 1 << (v & mask);
67  }
68 };
69 
70 /*
71  * Node:
72  * Each character in the tree is represented by a node.
73  *
74  * Note that the empty nodes (those with null data fields) are represented in the JSON data by having
75  * children with arbitrary length characters, where all but the last
76  * character represents an empty child. We unfold these, so the lookups
77  * can be carried out more simply.
78  * For Example, the JSON data might have a node at "M" with a child at
79  * "ozilla". We represent this as a string of descendents
80  * M, o, z, i, l, l, a.
81  * The node objects are small enough that this makes the time/space
82  * tradeoff well worth it.
83  */
84 
85 class Node {
86  // The JSON parsers for this type are allowed at its internals.
87  friend class NodeParse;
88  friend struct ChildrenParse;
89 
90  // Children, indexed by the extra character at the end of their substring
91 public:
93  childmap children;
94  Node();
95  ~Node();
96  NodeData *data;
97 
98  /* recursive search operations on the node: see below for details. */
99  template <typename T> void visitR(T &t, char key) const;
100 
101  // has this node any children?
102  bool isLeaf() const { return children.empty(); }
103 };
104 
105 class NodeData {
106  friend class NodeParse;
107  friend class Node;
108  attrvec attrs;
109  propset conflicts;
110  unsigned int nextTree;
111  unsigned int matchedDevices;
112  int resetWalkId;
113 public:
114  std::vector<size_t> *replacementStrings;
115  NodeData() : nextTree(0), matchedDevices(0), replacementStrings(0), resetWalkId(-1) {}
116  ~NodeData() { delete replacementStrings; }
117  const attrvec &getAttrs() const { return attrs; }
118  attrvec &getAttrs() { return attrs; }
119  const propset &getConflicts() const { return conflicts; }
120  propset &getConflicts() { return conflicts; }
121  unsigned getMatchedDeviceCount() const { return matchedDevices; }
122  unsigned getNextTreeMatchCount() const { return nextTree; }
123  const std::vector<size_t> *getReplacementStrings() const { return replacementStrings; }
124  int getResetWalkId() const { return resetWalkId; }
125 };
126 
127 /*
128  * visit this node, and all its children recursively, calling "pre"
129  * and "post" using a false return value to terminate the search.
130  * pre and post are passed the current node, and the character which
131  * identifies it in its parent.
132  */
133 template <typename T> void
134 Node::visitR(T &t, char key) const
135 {
136  if (!t.pre(key, this))
137  return;
138  for (childmap::const_iterator it = children.begin(); it != children.end(); ++it)
139  if ((*it).second)
140  (*it).second->visitR(t, (*it).first);
141  t.post(key, this);
142 }
143 
144 /*
145  * Searches for the substring of str from offset in the subtree of the
146  * current node.
147  * The search continues as follows:
148  * o call the supplied functor, and terminate the search if it returns false.
149  * o if we have reached the end of the string, return false.
150  * o find the child at the index specified for the character at str[offset]
151  * o if it exists, cal findR on it recursively, advancing offset.
152  */
153 
154 
155 
156 /*
157  * Utility to call find and visit on a specific tree in the atlas.
158  * Eliminates the "offset" parameter for clarity.
159  */
160 template <typename T, typename Allocator> size_t
161 dafind(const DeviceAtlas &atlas, T &t, TreeID tree, Allocator &a, const char *rostr, char **matched, char **unmatched)
162 {
163  const Node *n = atlas.roots[tree];
164  size_t len = strlen(rostr);
165  char *str;
166  a.allocate(str, len + 1);
167  strlcpy(str, rostr, len + 1);
168 
169  if (matched)
170  a.allocate(*matched, atlas.maxDepth + 1);
171 
172  Node::childmap::const_iterator it;
173  size_t offset;
174  size_t dataoff = 0;
175  char *pathp = matched ? *matched : 0;
176 
177  for (offset = 0;;++offset) {
178  NodeData *data = n->data;
179  // Don't do any heavy lifting if the node has no associated data.
180  if (data) {
181  if (data->getResetWalkId() > -1) {
182  char *pstr;
183  const char *resetWalkString = atlas.getResetWalkString((size_t)data->getResetWalkId()).c_str();
184  size_t resetWalkStringLen = strlen(resetWalkString);
185  size_t ostrlen = strlen(str + offset);
186  size_t pcount = atlas.getPropertyCount();
187  a.allocate(pstr, resetWalkStringLen + ostrlen + 1);
188  memcpy(pstr, resetWalkString, resetWalkStringLen);
189  memcpy(pstr + resetWalkStringLen, str + offset, ostrlen + 1);
190  if (matched)
191  *matched = 0;
192  if (unmatched)
193  *unmatched = 0;
194 
195  for (size_t i = 0; i < pcount; i ++)
196  t.setProperty(i, 0);
197 
198  return dafind(atlas, t, tree, a, pstr, matched, unmatched);
199  }
200  dataoff = offset;
201  if (!t(*n, str, offset) || str[offset] == 0)
202  break;
203  if ((atlas.flags & DeviceAtlas::NoREFold) == 0) {
204  const std::vector<size_t> *repls = data->replacementStrings;
205  if (repls)
206  atlas.doReplacements(repls, str, offset);
207  }
208  }
209  if (pathp)
210  *pathp++ = str[offset];
211  it = n->children.find(str[offset]);
212  if (it == n->children.end())
213  break;
214  n = (*it).second;
215  if (n == 0)
216  break;
217  }
218  if (matched)
219  (*matched)[dataoff] = 0;
220  if (unmatched)
221  *unmatched = str + dataoff;
222 
223  return offset;
224 }
225 
226 template <typename T> void
227 visit(const DeviceAtlas &atlas, T &t, TreeID tree)
228 {
229  atlas.roots[tree]->visitR(t, '.');
230 }
231 
232 // 3.1 extensions: "UAR"
233 class Rule {
234 public:
235  int property;
236  int regex;
237  int matchpos;
238  int value;
239  Rule()
240  : property(-1)
241  , regex(-1)
242  , matchpos(-1)
243  , value(-1)
244  {
245  }
246 };
247 
248 enum REType {
249  RE_REFINE = 0,
250  RE_SEARCH = 1,
251  RE_TYPES = 2
252 };
253 
254 class RuleSet {
255 public:
256  int re[RE_TYPES];
257  std::vector<Rule *> rules;
258  RuleSet();
259  ~RuleSet();
260 };
261 
262 class RuleGroup {
263 // all these properties must be true for a UA to consider it for this RG
264 public:
265  std::map<int, int> selectionCriteria; // maps from property to value
266  std::vector<RuleSet *> ruleSets;
267  RuleGroup();
268  ~RuleGroup();
269  const RuleSet *findSet(const UAR *, int, const char *text) const;
270  bool matchProperties(const DeliveryContext *ctx) const;
271 };
272 
273 class UAR {
274 public:
275  TaggedRegexMap allRegexes;
276  const std::vector<REInfo *> *langRegexes;
277  const std::vector<REInfo *> *defaultRegexes;
278  std::vector<int> skipProps; // if these properties are true, then don't apply UAR rules
279  std::vector<RuleGroup *> groups;
280  const REInfo *getRE(size_t re) const;
281  UAR();
282  ~UAR();
283 };
284 
285 enum Operator {
286  Equal,
287  NotEqual
288 };
289 
290 template <typename T> bool
291 testop(Operator op, const T &lhs, const T &rhs)
292 {
293  switch (op) {
294  case Equal: return lhs == rhs;
295  case NotEqual: return lhs != rhs;
296  default: { Exception err; err << "\"unknown\""; throw err; }
297  }
298 }
299 
300 
301 struct CPRule {
302  size_t property;
303  Value *value;
304 
305  Operator op;
306  bool test(const DeviceAtlas &da, const Value *candidate) const {
307  const Property &p = da.getPropertyDescr(property);
308  switch (p.type) {
309  case Bool: return testop<bool>(op, *value, *candidate);
310  case Integer: return testop<long>(op, *value, *candidate);
311  case String: return testop<std::string>(op, *value, *candidate);
312  default: throw Exception("unhandled property type");
313  }
314  }
315 };
316 
317 class CPRuleGroup {
318 public:
319  const char *ua;
320  typedef std::vector<CPRule> Rules;
321  Rules rules;
322  attrvec properties;
323  attrvec extraproperties;
324  CPRuleGroup() : ua(0) {}
325  void setProperty(size_t, const Value *) {}
326  bool match(const DeliveryContext &ctx) const;
327  bool operator() (const Node &node, const char *, size_t);
328 };
329 
330 std::ostream &operator<<(std::ostream &os, const CPRuleGroup &);
331 std::ostream &operator<<(std::ostream &os, const CPRule &);
332 std::ostream &operator<<(std::ostream &os, const Operator &);
333 
334 class CPR;
336  std::vector<const Value *> props;
337  DeliveryContext &ctx;
338  ClientPropertiesReceiver(DeliveryContext &ctx_) : ctx(ctx_) {}
339  void operator () (PropertyType type, const char *, const char *);
340 };
341 
342 /*
343  * q's Accept Language preference handling
344  * The first encountered is taken
345  */
346 
348  const char *code;
349  size_t len;
350  size_t minuspos;
351  float q;
352 };
353 
354 /*
355  * language value sanity check
356  */
357 
358 inline size_t
359 decodeLanguage(char *language, size_t *minuspos) {
360  *minuspos = 0;
361  if (*language == '*')
362  return 1;
363  char *p = language;
364  char *m = NULL;
365  size_t mlen;
366  if ((m = strchr(language, '-')) == NULL)
367  m = strchr(language, '_');
368 
369  if (m) {
370  mlen = strlen(m);
371  if (mlen == 1 || mlen > 3)
372  return 0;
373  *minuspos = m - p;
374  char *wp = p;
375  wp[*minuspos] = 0;
376  if (strlen(wp) > 2)
377  return 0;
378  p[*minuspos] = '-';
379  } else {
380  if (strlen(language) > 2)
381  return 0;
382  }
383  size_t mp = 0;
384  for(; *p; p++, mp++) {
385  if ((*p != '-' && !isalpha(*p) && !isspace(*p)))
386  break;
387  if (*minuspos > 0 && mp > *minuspos)
388  *p = toupper(*p);
389  }
390 
391  return mp;
392 }
393 
394 /*
395  * language preference handling
396  * the one chosen is related to q ordering (implicity or explicity)
397  */
398 template <typename Allocator>
399 inline const char*
400 stripQlanguage(const char *language, size_t *minuspos, Allocator &a) {
401  char *cp;
402  size_t plen = strlen(language);
403  a.allocate(cp, plen + 1);
404  strlcpy(cp, language, plen + 1);
405 
406  char *token, *ptr;
407  AcceptLanguagePreference apmax;
408  apmax.len = 0;
409  apmax.code = 0;
410  apmax.q = 0.0f;
411  apmax.minuspos = 0;
412 
413  while((token = strtok_r(cp, ",", &ptr)) != NULL) {
414  if (cp)
415  cp = 0;
416 
417  while (isspace(*token) && token ++);
418 
419  float q = 1.0f;
420  char *qstr, *code;
421  size_t codelen = 0;
422 
423  if ((qstr = strstr(token, ";")) != NULL) {
424  codelen = (qstr - token);
425  token[codelen] = 0;
426  if (qstr ++) {
427  while (isspace(*qstr) && qstr ++);
428  if (strncmp(qstr, "q=", 2) == 0) {
429  qstr += 2;
430  while (isspace(*qstr) && qstr ++);
431  if (*qstr != '*') {
432  float qtmp = strtod(qstr, 0);
433 
434  if (qtmp > 0.0f)
435  q = qtmp;
436  } else {
437  q = 0.0f;
438  }
439  }
440  }
441  }
442 
443  size_t tminuspos = 0;
444 
445  if ((codelen = decodeLanguage(token, &tminuspos)) > 0) {
446  a.allocate(code, codelen + 1);
447  strlcpy(code, token, codelen + 1);
448 
449  if (q > apmax.q) {
450  apmax.q = q;
451  apmax.len = codelen;
452  apmax.minuspos = tminuspos;
453  apmax.code = code;
454  } else if (q == apmax.q) {
455  if (apmax.code && tminuspos > 0 &&
456  strncmp(apmax.code, code, tminuspos) == 0 &&
457  codelen > apmax.len) {
458  apmax.len = codelen;
459  apmax.minuspos = tminuspos;
460  apmax.code = code;
461  }
462  }
463  }
464  }
465 
466  const char *finallanguage = (apmax.code ? (*apmax.code == '*' ? 0 : apmax.code) : 0);
467  *minuspos = (apmax.code ? apmax.minuspos : 0);
468 
469  return finallanguage;
470 }
471 
472 template <typename T, typename Allocator>
473 void decodeHeuristic(const char *p, T &cprec, Allocator &a) // XXX: template on ClientPropertiesReceiver
474 {
475  PropertyType type;
476  char c;
477  char *cp;
478  size_t plen = strlen(p);
479  a.allocate(cp, plen + 1);
480  strlcpy(cp, p, plen + 1);
481 
482  using namespace Mobi::Mtld::Util;
483 
484  char *token, *ptr;
485 
486  while ((token = strtok_r(cp, "|", &ptr)) != NULL) {
487  if (cp)
488  cp = 0;
489  char key[124], *kp = key;
490  char escapedvalue[124], *ep = escapedvalue;
491  switch ((c = nextchar((const char *&)token, Util::SKIPQUOTE | Util::NULLOK))) {
492  case 'b': type = Bool; break;
493  case 'i': type = Integer; break;
494  case 's': type = String; break;
495  case 0: return; // that's all folks.
496  default: cprec.props.clear(); return;
497  }
498 
499  char *sptr, *tmpkp, *tmpvp;
500  tmpkp = strtok_r(token, ":", &sptr);
501  tmpvp = strtok_r(NULL, ":", &sptr);
502  if (!tmpvp)
503  continue;
504 
505  size_t m = 0;
506  char *k = kp;
507  for (char *p = tmpkp; m++ < (sizeof(key)) - 1 && *p; ++p)
508  *k++ = *p;
509  *k = 0;
510 
511  m = 0;
512  for (const char *p = tmpvp; m++ < (sizeof(escapedvalue) - 1) && *p; ++p) {
513  switch (*p) {
514  case '\"': ep += snprintf(ep, sizeof("&quote;") + 1, "&quote;"); break;
515  case '&': ep += snprintf(ep, sizeof("&amp;") + 1, "&amp;"); break;
516  case '<': ep += snprintf(ep, sizeof("&lt;") + 1, "&lt;"); break;
517  case '>': ep += snprintf(ep, sizeof("&gt;") + 1, "&gt;"); break;
518  default: *ep++ = *p; break;
519  }
520  }
521  *ep = 0;
522 
523  const char * i;
524  for (i = kp; *i && (isalnum(*i) || *i == '.'); ++i);
525 
526  if (*i == 0 && i != kp)
527  cprec(type, kp, escapedvalue);
528  }
529 }
530 
531 class CPR {
532 public:
533  typedef std::vector<CPRuleGroup> RuleGroups;
534  RuleGroups ruleGroups;
535  const CPRuleGroup *getMatchingCPRules(const DeliveryContext &ctx) const;
536 };
537 
538 
539 }}}
540 #endif // devatlas_priv_h
Definition: devatlas_priv.h:301
Definition: devatlas.h:105
Definition: devatlas_priv.h:531
Definition: exception.h:16
Definition: devatlas_priv.h:38
Definition: dajson.cpp:244
Definition: util.h:39
Definition: dajson.cpp:187
Definition: common.h:41
Definition: devatlas_priv.h:335
Definition: devatlas_priv.h:85
Definition: devatlas_priv.h:105
Definition: common.h:190
Definition: devatlas_priv.h:233
Definition: devatlas_priv.h:10
Definition: devatlas_priv.h:16
Definition: devatlas_priv.h:254
Definition: devatlas_priv.h:273
Definition: devatlas_priv.h:347
Definition: binary.h:13
Definition: devatlas.h:218
Definition: devatlas_priv.h:262
Definition: devatlas_priv.h:317
Definition: regex.h:10
Definition: common.h:140