Parcourir la source

[2096] Help for the RDataReader interface

Including the expected usage scenarios.

Also, the interface was slightly refined - renaming of things, adding
support for the RRSig iteration.
Michal 'vorner' Vaner il y a 12 ans
Parent
commit
cd1a4d1452
1 fichiers modifiés avec 134 ajouts et 7 suppressions
  1. 134 7
      src/lib/datasrc/memory/rdata_reader.h

+ 134 - 7
src/lib/datasrc/memory/rdata_reader.h

@@ -18,51 +18,178 @@
 #include <boost/function.hpp>
 
 namespace isc {
+// Some forward declarations
 namespace dns{
 class LabelSequence;
 class RRClass;
 class RRType;
 }
+
 namespace datasrc {
 namespace memory {
 
+/// \brief Class to read serialized rdata
+///
+/// This class allows you to read the data encoded by RDataEncoder.
+/// It is rather low-level -- it provides sequence of data fields
+/// and names. It does not give you convenient Rdata or RRset class.
+///
+/// It allows two types of operation. First one is by providing callbacks
+/// to the constructor and then iterating by repeatedly calling next, or
+/// calling iterate once. The callbacks are then called with each part of
+/// the data.
+///
+/// \code
+/// void handleLabel(const dns::LabelSequence& label, unsigned int flags) {
+///     ...
+/// }
+/// void handleData(const uint8_t* data, size_t size) {
+///     ...
+/// }
+///
+/// RDataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+///                    &handleLabel, handleData);
+/// reader.iterate();
+/// \endcode
+///
+/// The other way is to use the return value of next() and loop through
+/// it manually. It has the inconvenience of having the type condition, but
+/// the code is in one place. The used way is matter of personal preferrence,
+/// there's no much difference on the technical side.
+///
+/// \code
+/// RDataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+///                    &handleLabel, handleData);
+/// RDataReader::Result data;
+/// while (data = reader.next()) {
+///     switch(data.type()) {
+///         case RDataReader::NAME:
+///             ...
+///             break;
+///         case RDataReader::DATA:
+///             ...
+///             break;
+///         default: assert(0); // Can not happen
+///     }
+/// }
+/// \endcode
 class RDataReader {
 public:
-    // The sequence and attributes
+    /// \brief Function called on each name encountered in the data.
     typedef boost::function<void(const dns::LabelSequence&, unsigned int)>
         NameAction;
+    /// \brief Function called on each data field in the data.
     typedef boost::function<void(const uint8_t*, size_t)> DataAction;
 
-    // For the default parameters.
+    /// \brief a NameAction that does nothing.
+    ///
+    /// This is a NameAction function that does nothing. It is used
+    /// as a default in the constructor.
     static void emptyNameAction(const dns::LabelSequence& label,
                                 unsigned int attributes);
+    /// \brief a DataAction that does nothing.
+    ///
+    /// This is a DataAction function that does nothing. It is used
+    /// as a default in the constructor.
     static void emptyDataAction(const uint8_t* data, size_t size);
 
+    /// \brief Constructor
+    ///
+    /// This constructs the reader on top of some serialized data.
+    /// It does not copy the data, you have to make sure the data
+    /// is valid for the whole life of this object and that they
+    /// don't change.
+    ///
+    /// \param rrclass The class the encoded rdata belongs to.
+    /// \param rrtype The type of the encode rdata.
+    /// \param size Number of bytes the data have in serialized form.
+    /// \param data The actual data.
+    /// \param name_action The callback to be called on each encountered name.
+    /// \param data_action The callback to be called on each data chunk.
     RDataReader(const dns::RRClass& rrclass, const dns::RRType& rrtype,
                 size_t size, const uint8_t* data,
                 const NameAction& name_action = &emptyNameAction,
                 const DataAction& data_action = &emptyDataAction);
 
-    enum Action {
-        NAME,
-        DATA,
-        END
+    /// \brief The type of data returned from this iteration.
+    enum DataType {
+        NAME, ///< This iteration returns domain label
+        DATA, ///< This iteration returns unstructuder data
+        END   ///< No more data to return
     };
 
+    /// \brief Data from one iteration
+    ///
+    /// Each time you call next() or nextSig(), it returns some data.
+    /// This holds the data.
     class Result {
     public:
-        Action action() const;
+        /// \brief The type of data returned.
+        DataType type() const;
+        /// \brief The raw data.
+        ///
+        /// This is only valid if type() == DATA.
         const uint8_t* data() const;
+        /// \brief The size of the raw data.
+        ///
+        /// This is the number of bytes the data takes. It is valid only
+        /// if type() == DATA.
         size_t size() const;
+        /// \brief The domain label.
+        ///
+        /// This holds the domain label. It is only valid if type() == NAME.
         const dns::LabelSequence& sequence;
+        /// \brief If there are data returned.
+        ///
+        /// This returns if there are any data at all returned. This is
+        /// equivalent to action != END, but it allows for more convenient
+        /// code of a loop through the data.
         operator bool() const {
             return (action() != END);
         }
     };
 
+    /// \brief Step to next piece of data.
+    ///
+    /// This returns the next available data. Also, the apropriate hook
+    /// (name_action or data_action, depending on the data type) as passed
+    /// to the constructor is called.
+    ///
+    /// If there are no more data, a Result with type END is returned and
+    /// no callback is called.
     Result next();
+    /// \brief Call next() until the end.
+    ///
+    /// This is just convenience method to iterate through all the data.
+    /// It calls next until it reaches the end (it does not revind before,
+    /// therefore if you already called next() yourself, it does not start
+    /// at the beginning).
+    ///
+    /// The method only makes sense if you set the callbacks in constructor.
+    void iterate() {
+        while (next()) { }
+    }
+    /// \brief Step to next piece of RRSig data.
+    ///
+    /// This is almost the same as next(), but it iterates through the
+    /// associated RRSig data, not the data for the given RRType.
+    Result nextSig();
+    /// \brief Iterate through all RRSig data.
+    ///
+    /// This is almost the same as iterate(), but it iterates through the
+    /// RRSig data instead.
+    void iterateSig() {
+        while (nextSig()) { }
+    }
+    /// \brief Rewind the iterator to the beginnig of data.
+    ///
+    /// The following next() and nextSig() will start iterating from the
+    /// beginning again.
     void rewind();
 
+    /// \brief Returns the size of associated data.
+    ///
+    /// This just returns whatever was passed to the constructor as size.
     size_t getSize() const;
 };