The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 09
META.json 22
META.yml 22
Makefile.PL 11
Nmsg.xs 212120
lib/Net/Nmsg/IO.pm 107
lib/Net/Nmsg/Input.pm 45
lib/Net/Nmsg/Msg.pm 58
lib/Net/Nmsg/Output.pm 920
lib/Net/Nmsg/Typemap.pm 44
lib/Net/Nmsg/Util.pm 11
lib/Net/Nmsg.pm 11
perl_math_int64.c 44
perl_math_int64.h 44
14 files changed (This is a version diff) 259188
@@ -1,5 +1,14 @@
 Revision history for Net::Nmsg
 
+0.15  Thu Mar 12 10:28:04 EDT 2015
+    - moved to Math::Int64 v0.51
+
+0.14  Thu Mar  5 15:18:39 EST 2015
+    - initialization of int64 module
+    - fixed calls to add_output_sock() and add_input_channel()
+    - fixed fetch for repeated field values
+    - fixed some input option settings
+
 0.13  Fri Feb 20 16:11:02 EST 2015
     - fully integrated Math::Int64 and hex parsing
     - added some exceptions for certain failure modes
@@ -32,11 +32,11 @@
       },
       "runtime" : {
          "requires" : {
-            "Math::Int64" : "0",
+            "Math::Int64" : "0.51",
             "NetAddr::IP::Util" : "0"
          }
       }
    },
    "release_status" : "stable",
-   "version" : "0.13"
+   "version" : "0.15"
 }
@@ -18,6 +18,6 @@ no_index:
     - t
     - inc
 requires:
-  Math::Int64: 0
+  Math::Int64: 0.51
   NetAddr::IP::Util: 0
-version: 0.13
+version: 0.15
@@ -66,7 +66,7 @@ WriteMakefile(
   ABSTRACT_FROM => 'lib/Net/Nmsg.pm',
   AUTHOR        => 'Matt Sisk <sisk@cert.org>',
   PREREQ_PM => {
-    'Math::Int64'       => 0,
+    'Math::Int64'       => '0.51',
     'NetAddr::IP::Util' => 0,
   },
   %options
@@ -86,7 +86,7 @@ typedef union {
     int64_t     i64;
     int32_t     i32;
     int16_t     i16;
-    unsigned    en;
+    int         en;
     double      dbl;
     bool        boo;
 } nmsg_field_val_u;
@@ -275,42 +275,12 @@ io_closed_callback(struct nmsg_io_close_event *ce) {
 }
 
 groknum_err_t
-_xs_pack_uint16_int64(pTHX_ SV *rv, uint16_t *val) {
-    SV      *sv;
-    int64_t  i64;
-
-    // note: assumes valid Math::Int64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    i64 = SvI64(rv);
-    if (i64 < 0 || i64 > UINT16_MAX)
-        return GROKNUM_ERR_U16_OVERFLOW;
-    *val = (uint16_t)i64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
-_xs_pack_uint16_uint64(pTHX_ SV *rv, uint16_t *val) {
-    SV       *sv;
-    uint64_t  u64;
-
-    // note: assumes valid Math::Uint64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    u64 = SvU64(rv);
-    if (u64 > UINT16_MAX)
-        return GROKNUM_ERR_U16_OVERFLOW;
-    *val = (uint16_t)u64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
 _xs_pack_uint16_int(pTHX_ SV *sv, uint16_t *val) {
     UV uv;
     IV iv;
     NV nv;
+    int64_t  i64;
+    uint64_t u64;
     groknum_err_t rv = GROKNUM_OK;
 
     if (SvIOK_UV(sv)) {
@@ -332,10 +302,16 @@ _xs_pack_uint16_int(pTHX_ SV *sv, uint16_t *val) {
         *val = (uint16_t)SvUV(sv);
     }
     else if (SvU64OK(sv)) {
-        rv = _xs_pack_uint16_uint64(aTHX_ sv, val);
+        u64 = SvU64(sv);
+        if (u64 > UINT16_MAX)
+            rv = GROKNUM_ERR_U16_OVERFLOW;
+        *val = (uint16_t)u64;
     }
     else if (SvI64OK(sv)) {
-        rv = _xs_pack_uint16_int64(aTHX_ sv, val);
+        i64 = SvI64(sv);
+        if (i64 < 0 || i64 > UINT16_MAX)
+            rv = GROKNUM_ERR_U16_OVERFLOW;
+        *val = (uint16_t)i64;
     }
     else
         rv = GROKNUM_ERR_NAN;
@@ -391,42 +367,12 @@ _xs_make_uint16(pTHX_ SV *sv) {
 }
 
 groknum_err_t
-_xs_pack_int16_int64(pTHX_ SV *rv, int16_t *val) {
-    SV      *sv;
-    int64_t  i64;
-
-    // note: assumes valid Math::Int64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    i64 = SvI64(rv);
-    if (i64 < INT16_MIN || i64 > INT16_MAX)
-        return GROKNUM_ERR_I16_OVERFLOW;
-    *val = (int16_t)i64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
-_xs_pack_int16_uint64(pTHX_ SV *rv, int16_t *val) {
-    SV       *sv;
-    int64_t   i64;
-
-    // note: assumes valid Math::Int64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    i64 = SvI64(rv);
-    if (i64 > UINT16_MAX)
-        return GROKNUM_ERR_I16_OVERFLOW;
-    *val = (int16_t)i64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
 _xs_pack_int16_int(pTHX_ SV *sv, int16_t *val) {
     UV uv;
     IV iv;
     NV nv;
+    int64_t  i64;
+    uint64_t u64;
     groknum_err_t rv = GROKNUM_OK;
 
     if (SvIOK_UV(sv)) {
@@ -448,10 +394,16 @@ _xs_pack_int16_int(pTHX_ SV *sv, int16_t *val) {
         *val = (int16_t)SvIV(sv);
     }
     else if (SvU64OK(sv)) {
-        rv = _xs_pack_int16_uint64(aTHX_ sv, val);
+        u64 = SvU64(sv);
+        if (u64 > INT16_MAX)
+            rv = GROKNUM_ERR_I16_OVERFLOW;
+        *val = (int16_t)u64;
     }
     else if (SvI64OK(sv)) {
-        rv = _xs_pack_int16_int64(aTHX_ sv, val);
+        i64 = SvI64(sv);
+        if (i64 < INT16_MIN || i64 > INT16_MAX)
+            rv = GROKNUM_ERR_I16_OVERFLOW;
+        *val = (int16_t)i64;
     }
     else
         rv = GROKNUM_ERR_NAN;
@@ -507,42 +459,12 @@ _xs_make_int16(pTHX_ SV *sv) {
 }
 
 groknum_err_t
-_xs_pack_uint32_int64(pTHX_ SV *rv, uint32_t *val) {
-    SV      *sv;
-    int64_t  i64;
-
-    // note: assumes valid Math::Int64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    i64 = SvI64(rv);
-    if (i64 < 0 || i64 > UINT32_MAX)
-        return GROKNUM_ERR_U32_OVERFLOW;
-    *val = (uint32_t)i64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
-_xs_pack_uint32_uint64(pTHX_ SV *rv, uint32_t *val) {
-    SV       *sv;
-    uint64_t  u64;
-
-    // note: assumes valid Math::Uint64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    u64 = SvU64(rv);
-    if (u64 > UINT32_MAX)
-        return GROKNUM_ERR_U32_OVERFLOW;
-    *val = (uint32_t)u64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
 _xs_pack_uint32_int(pTHX_ SV *sv, uint32_t *val) {
     UV uv;
     IV iv;
     NV nv;
+    int64_t  i64;
+    uint64_t u64;
     groknum_err_t rv = GROKNUM_OK;
 
     if (SvIOK_UV(sv)) {
@@ -564,10 +486,16 @@ _xs_pack_uint32_int(pTHX_ SV *sv, uint32_t *val) {
         *val = (uint32_t)SvUV(sv);
     }
     else if (SvU64OK(sv)) {
-        rv = _xs_pack_uint32_uint64(aTHX_ sv, val);
+        u64 = SvU64(sv);
+        if (u64 > UINT32_MAX)
+            rv = GROKNUM_ERR_U32_OVERFLOW;
+        *val = (uint32_t)u64;
     }
     else if (SvI64OK(sv)) {
-        rv = _xs_pack_uint32_int64(aTHX_ sv, val);
+        i64 = SvI64(sv);
+        if (i64 < 0 || i64 > UINT32_MAX)
+            rv = GROKNUM_ERR_U32_OVERFLOW;
+        *val = (uint32_t)i64;
     }
     else
         rv = GROKNUM_ERR_NAN;
@@ -623,42 +551,12 @@ _xs_make_uint32(pTHX_ SV *sv) {
 }
 
 groknum_err_t
-_xs_pack_int32_int64(pTHX_ SV *rv, int32_t *val) {
-    SV      *sv;
-    int64_t  i64;
-
-    // note: assumes valid Math::Int64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    i64 = SvI64(rv);
-    if (i64 < INT32_MIN || i64 > INT32_MAX)
-        return GROKNUM_ERR_I32_OVERFLOW;
-    *val = (int32_t)i64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
-_xs_pack_int32_uint64(pTHX_ SV *rv, int32_t *val) {
-    SV       *sv;
-    uint64_t  u64;
-
-    // note: assumes valid Math::Uint64
-    sv = SvRV(rv);
-    if (!sv || (SvTYPE(sv) != SVt_PVMG))
-        return GROKNUM_ERR_NVREF;
-    u64 = SvU64(rv);
-    if (u64 > INT32_MAX)
-        return GROKNUM_ERR_I32_OVERFLOW;
-    *val = (uint32_t)u64;
-    return GROKNUM_OK;
-}
-
-groknum_err_t
 _xs_pack_int32_int(pTHX_ SV *sv, int32_t *val) {
     UV uv;
     IV iv;
     NV nv;
+    int64_t  i64;
+    uint64_t u64;
     groknum_err_t rv = GROKNUM_OK;
 
     if (SvIOK_UV(sv)) {
@@ -680,10 +578,16 @@ _xs_pack_int32_int(pTHX_ SV *sv, int32_t *val) {
         *val = (int32_t)SvIV(sv);
     }
     else if (SvU64OK(sv)) {
-        rv = _xs_pack_int32_uint64(aTHX_ sv, val);
+        u64 = SvU64(sv);
+        if (u64 > INT32_MAX)
+            rv = GROKNUM_ERR_I32_OVERFLOW;
+        *val = (uint32_t)u64;
     }
     else if (SvI64OK(sv)) {
-        rv = _xs_pack_int32_int64(aTHX_ sv, val);
+        i64 = SvI64(sv);
+        if (i64 < INT32_MIN || i64 > INT32_MAX)
+            rv = GROKNUM_ERR_I32_OVERFLOW;
+        *val = (int32_t)i64;
     }
     else
         rv = GROKNUM_ERR_NAN;
@@ -905,55 +809,51 @@ _xs_field_to_sv(pTHX_ void *data, size_t len, nmsg_msgmod_field_type type) {
 uint8_t *
 _xs_sv_to_field(pTHX_ SV *sv, nmsg_msgmod_field_type type,
                 nmsg_field_val_u *data, size_t *len) {
-    UV uv;
-    IV iv;
-    NV nv;
-
     switch (type) {
-    case nmsg_msgmod_ft_int16:
-        data->i16 = _xs_make_int16(aTHX_ sv);
-        *len = sizeof(int16_t);
-        break;
-    case nmsg_msgmod_ft_uint16:
-        data->u16 = _xs_make_uint16(aTHX_ sv);
-        *len = sizeof(uint16_t);
-        break;
-    case nmsg_msgmod_ft_int32:
-        data->i32 = _xs_make_int32(aTHX_ sv);
-        *len = sizeof(int32_t);
-        break;
-    case nmsg_msgmod_ft_uint32:
-        data->u32 = _xs_make_uint32(aTHX_ sv);
-        *len = sizeof(uint32_t);
-        break;
-    case nmsg_msgmod_ft_int64:
-        data->i64 = _xs_make_int64(aTHX_ sv);
-        *len = sizeof(int64_t);
-        break;
-    case nmsg_msgmod_ft_uint64:
-        data->u64 = _xs_make_uint64(aTHX_ sv);
-        *len = sizeof(uint64_t);
-        break;
-    case nmsg_msgmod_ft_enum:
-        data->en = (unsigned)SvUV(sv);
-        *len = sizeof(unsigned);
-        break;
-    case nmsg_msgmod_ft_double:
-        data->dbl = (double)SvNV(sv);
-        break;
-    case nmsg_msgmod_ft_bool:
-        data->boo = (bool)SvTRUE(sv);
-        break;
-    case nmsg_msgmod_ft_string:
-    case nmsg_msgmod_ft_mlstring:
-        data = (void *)SvPV(sv, *len);
-        *len += 1;
-        break;
-    //case nmsg_msgmod_ft_ip:
-    //case nmsg_msgmod_ft_bytes:
-    default:
-        data = (void *)SvPV(sv, *len);
-        break;
+        case nmsg_msgmod_ft_int16:
+            data->i16 = _xs_make_int16(aTHX_ sv);
+            *len = sizeof(int16_t);
+            break;
+        case nmsg_msgmod_ft_uint16:
+            data->u16 = _xs_make_uint16(aTHX_ sv);
+            *len = sizeof(uint16_t);
+            break;
+        case nmsg_msgmod_ft_int32:
+            data->i32 = _xs_make_int32(aTHX_ sv);
+            *len = sizeof(int32_t);
+            break;
+        case nmsg_msgmod_ft_uint32:
+            data->u32 = _xs_make_uint32(aTHX_ sv);
+            *len = sizeof(uint32_t);
+            break;
+        case nmsg_msgmod_ft_int64:
+            data->i64 = _xs_make_int64(aTHX_ sv);
+            *len = sizeof(int64_t);
+            break;
+        case nmsg_msgmod_ft_uint64:
+            data->u64 = _xs_make_uint64(aTHX_ sv);
+            *len = sizeof(uint64_t);
+            break;
+        case nmsg_msgmod_ft_enum:
+            data->en = (int)SvIV(sv);
+            *len = sizeof(int);
+            break;
+        case nmsg_msgmod_ft_double:
+            data->dbl = (double)SvNV(sv);
+            break;
+        case nmsg_msgmod_ft_bool:
+            data->boo = (bool)SvTRUE(sv);
+            break;
+        case nmsg_msgmod_ft_string:
+        case nmsg_msgmod_ft_mlstring:
+            data = (void *)SvPV(sv, *len);
+            *len += 1;
+            break;
+        //case nmsg_msgmod_ft_ip:
+        //case nmsg_msgmod_ft_bytes:
+        default:
+            data = (void *)SvPV(sv, *len);
+            break;
     }
     return((uint8_t *)data);
 }
@@ -1048,6 +948,8 @@ BOOT:
 
     MCE(NMSG_ALIAS_OPERATOR,    nmsg_alias_operator);
     MCE(NMSG_ALIAS_GROUP,       nmsg_alias_group   );
+
+    MATH_INT64_BOOT;
 }
 
 
@@ -1072,6 +974,7 @@ void
 nmsg_set_debug(debug)
     int debug
 
+
 MODULE = Net::Nmsg		PACKAGE = Net::Nmsg::Util   PREFIX = nmsg_
 
 void
@@ -1148,8 +1051,7 @@ get_timestring()
     if (tstr == NULL)
         croak("problem allocating time string");
     mXPUSHs(newSVpv(tstr, 0));
-    free(tstr);
-
+    Safefree(tstr);
 
 
 MODULE = Net::Nmsg  PACKAGE = Net::Nmsg::Util   PREFIX = nmsg_msgmod_
@@ -1243,7 +1145,6 @@ _add_input(THIS, input, ...)
             croak("not a reference");
     }
     res = nmsg_io_add_input(THIS, input, user);
-    //fprintf(stderr, "added io input (%d)\n", res);
     if (res != nmsg_res_success)
         croak("nmsg_io_add_input failed: %s", nmsg_res_lookup(res));
     if (user != NULL)
@@ -1530,7 +1431,6 @@ read(THIS, blocking_io=true)
         SAVE_signals = PL_signals;
         PL_signals |= PERL_SIGNALS_UNSAFE_FLAG;
         res = nmsg_input_read(THIS, &m);
-        //fprintf(stderr, "xs read %p %s\n", THIS, nmsg_res_lookup(res));
         PL_signals = SAVE_signals;
         switch (res) {
         case (nmsg_res_success):
@@ -1562,7 +1462,7 @@ loop(THIS, cb, count)
     PL_signals |= PERL_SIGNALS_UNSAFE_FLAG;
     res = nmsg_input_loop(THIS, count, output_callback, (void *)cb);
     PL_signals = SAVE_signals;
-    if (res != nmsg_res_success || res != nmsg_res_eof)
+    if (res != nmsg_res_success && res != nmsg_res_eof)
         croak("nmsg_input_loop() failed(%d): %s", res, nmsg_res_lookup(res));
     RETVAL = res;
     OUTPUT:
@@ -1632,19 +1532,17 @@ nmsg_output_set_endline(THIS, value)
 	const char *value
 
 void
-_set_rate(THIS, rate, freq)
-	Net::Nmsg::XS::output   THIS
-    unsigned    rate
-    unsigned    freq
+set_rate(THIS, rate, freq)
+    Net::Nmsg::XS::output THIS
+    unsigned rate
+    unsigned freq
     PREINIT:
-	nmsg_rate_t nr;
+    nmsg_rate_t nr;
     CODE:
     nr = nmsg_rate_init(rate, freq);
     if (nr == NULL)
         croak("rate error %d/%d", rate, freq);
     nmsg_output_set_rate(THIS, nr);
-    mXPUSHu(rate);
-    mXPUSHu(freq);
 
 void
 nmsg_output_set_zlibout(THIS, value)
@@ -1667,14 +1565,10 @@ nmsg_output_set_source(THIS, value)
 	unsigned    value
 
 void
-_set_filter_msgtype(THIS, vid, mid)
+nmsg_output_set_filter_msgtype(THIS, vid, mid)
 	Net::Nmsg::XS::output   THIS
 	unsigned    vid
 	unsigned    mid
-    CODE:
-    nmsg_output_set_filter_msgtype(THIS, vid, mid);
-    mXPUSHu(vid);
-    mXPUSHu(mid);
 
 void
 _write(THIS, msg)
@@ -1692,7 +1586,6 @@ _write(THIS, msg)
         croak("nmsg_output_write() failed: %s", nmsg_res_lookup(res));
 
 
-
 MODULE = Net::Nmsg  PACKAGE = Net::Nmsg::XS::msg PREFIX = nmsg_message_
 
 PROTOTYPES: ENABLE
@@ -1761,8 +1654,6 @@ get_field(THIS, field, v_idx = 0)
     if (res == nmsg_res_success && data != NULL) {
         res = nmsg_message_get_field_type(THIS, field, &type);
         if (res == nmsg_res_success) {
-            // mXPUSHs(newSV(0));
-            // _xs_field_to_sv(aTHX_ data, len, type, ST(0));
             mXPUSHs(_xs_field_to_sv(aTHX_ data, len, type));
         }
         else
@@ -1771,6 +1662,27 @@ get_field(THIS, field, v_idx = 0)
     }
 
 void
+get_field_vals(THIS, field)
+    Net::Nmsg::XS::msg  THIS
+    const char         *field
+    PREINIT:
+    nmsg_res                res;
+    size_t                  len;
+    void                   *data;
+    nmsg_msgmod_field_type  type;
+    int                     i;
+    PPCODE:
+    res = nmsg_message_get_field_type(THIS, field, &type);
+    if (res != nmsg_res_success)
+        croak("nmsg_message_get_field_type failed: %s", nmsg_res_lookup(res));
+    for (i = 0; ; i++) {
+        res = nmsg_message_get_field(THIS, field, i, &data, &len);
+        if (res != nmsg_res_success || data == NULL)
+            break;
+        mXPUSHs(_xs_field_to_sv(aTHX_ data, len, type));
+    }
+
+void
 get_field_by_idx(THIS, f_idx, v_idx = 0)
     Net::Nmsg::XS::msg  THIS
     unsigned            f_idx
@@ -1785,8 +1697,6 @@ get_field_by_idx(THIS, f_idx, v_idx = 0)
     if (res == nmsg_res_success) {
         res = nmsg_message_get_field_type_by_idx(THIS, f_idx, &type);
         if (res == nmsg_res_success && data != NULL) {
-            // mXPUSHs(newSV(0));
-            // _xs_field_to_sv(aTHX_ data, len, type, ST(0));
             mXPUSHs(_xs_field_to_sv(aTHX_ data, len, type));
         }
         else if (res != nmsg_res_success)
@@ -1807,12 +1717,10 @@ get_field_vals_by_idx(THIS, f_idx)
     PPCODE:
     res = nmsg_message_get_field_type_by_idx(THIS, f_idx, &type);
     if (res == nmsg_res_success) {
-        for (i = 0; i >= 0; i++) {
+        for (i = 0; ; i++) {
             res = nmsg_message_get_field_by_idx(THIS, f_idx, i, &data, &len);
             if (res != nmsg_res_success || data == NULL)
                 break;
-            // mXPUSHs(newSV(0));
-            // _xs_field_to_sv(aTHX_ data, len, type, ST(i));
             mXPUSHs(_xs_field_to_sv(aTHX_ data, len, type));
         }
     }
@@ -2029,7 +1937,7 @@ message_to_pres(THIS, endline)
     if (res != nmsg_res_success)
         goto out;
     mXPUSHs(newSVpv(pres, 0));
-    free(pres);
+    Safefree(pres);
     out:
     pthread_mutex_unlock(&presentation_lock);
     if (res != nmsg_res_success)
@@ -2123,7 +2031,7 @@ get_field_enum_descr_by_idx(THIS, f_idx)
     PPCODE:
     res = nmsg_message_get_field_type_by_idx(THIS, f_idx, &type);
     if (res == nmsg_res_success && type == nmsg_msgmod_ft_enum) {
-        for (v = 0; v; v++) {
+        for (v = 0;  ; v++) {
             res = nmsg_message_enum_value_to_name_by_idx(
                     THIS, f_idx, v, &name);
             if (res != nmsg_res_success)
@@ -50,9 +50,6 @@ use Carp;
 
 use Symbol ();
 
-use Data::Dumper;
-$Data::Dumper::Indent = 1;
-
 use Net::Nmsg::Util qw( :io :vendor :sniff :channel :alias DEBUG );
 use Net::Nmsg::Input;
 use Net::Nmsg::Output;
@@ -211,7 +208,6 @@ sub loop {
   my $xs = $self->_xs;
   $xs->loop();
   $self->_init;
-  $self;
 }
 
 ### io wraps
@@ -219,12 +215,10 @@ sub loop {
 sub _wrap_io {
   my $self = shift;
   my($io, %opt) = @_;
-  #print STDERR "WRAP IO HERE: ", Dumper($io, \%opt);
   my $callbacks = $self->_callbacks;
   if (my $close_cb = $opt{close_cb}) {
     $callbacks->{$io} = sub {
       my $close_type = shift;
-      #print STDERR "  SPEC: $io : ", $io->_spec, "\n";
       return \0 if $close_type == NMSG_CLOSE_TYPE_EOF;
       my $old_xs = $io->_xs;
       $close_cb->($io);
@@ -288,7 +282,7 @@ sub add_input  {
     $self->add_input_sock($io, @_);
   }
   elsif (is_channel($io)) {
-    $self->add_channel_input($io, @_);
+    $self->add_input_channel($io, @_);
   }
   else {
     $self->_add_input_io($self->IO_INPUT->open($io, @_), @_);
@@ -356,7 +350,10 @@ sub add_output  {
   my $io   = shift;
   return $self->_add_output_io($io, @_) if $self->_is_io($io);
   if (! ref $io && expand_socket_spec($io)) {
-    $self->add_output_nmsg($io, @_);
+    $self->add_output_sock($io, @_);
+  }
+  elsif (is_channel($io)) {
+    $self->add_output_channel($io, @_);
   }
   else {
     $self->_add_output_io($self->IO_OUTPUT->open($io, @_), @_);
@@ -368,7 +365,7 @@ sub add_output_channel {
   my $chan = shift;
   my @sock = channel_lookup($chan);
   croak "not a channel '$chan'" unless @sock;
-  $self->add_output_nmsg($_, @_) foreach @sock;
+  $self->add_output_sock($_, @_) foreach @sock;
 }
 *add_output_chalias = \&add_output_channel;
 
@@ -626,7 +623,7 @@ message objects.
 
 =head1 SEE ALSO
 
-L<Net::Nmsg::Input>, L<Net::Nmsg::Output>, L<Net::Nmsg::Msg>, L<nmsgtool(1)>
+L<Net::Nmsg>, L<Net::Nmsg::Input>, L<Net::Nmsg::Output>, L<Net::Nmsg::Msg>, L<nmsgtool(1)>
 
 =head1 AUTHOR
 
@@ -117,8 +117,9 @@ sub _map_opts {
   my %opt  = @_;
   my $vendor  = delete $opt{filter_vendor};
   my $msgtype = delete $opt{filter_msgtype};
-  return %opt unless defined $vendor || defined $msgtype;
-  $opt{filter_msgtype} = [$vendor, $msgtype];
+  if (defined $vendor || defined $msgtype) {
+    $opt{filter_msgtype} = [$vendor, $msgtype];
+  }
   %opt;
 }
 
@@ -391,7 +392,7 @@ Promiscuous mode (live interface only)
 
 =item open_file($spec, %options)
 
-Opens an input in nmsg format, as specified by file name, file handle.
+Opens an input in nmsg format, as specified by file name or file handle.
 
 =item open_sock($spec, %options)
 
@@ -468,7 +469,7 @@ many messages have been returned via the callback.
 
 =head1 SEE ALSO
 
-L<Net::Nmsg::IO>, L<Net::Nmsg::Output>, L<Net::Nmsg::Msg>, L<nmsgtool(1)>
+L<Net::Nmsg>, L<Net::Nmsg::IO>, L<Net::Nmsg::Output>, L<Net::Nmsg::Msg>, L<nmsgtool(1)>
 
 =head1 AUTHOR
 
@@ -164,7 +164,7 @@ sub operator {
     $self->_unpack;
   }
   my($k, $v) = operator_lookup($self->[OPR]);
-  $v || '<UNKNOWN>';
+  $v;
 }
 
 sub group {
@@ -178,7 +178,7 @@ sub group {
     $self->_unpack;
   }
   my($k, $v) = group_lookup($self->[GRP]);
-  $v || '<UNKNOWN>';
+  $v;
 }
 
 sub time {
@@ -224,7 +224,10 @@ sub headers_as_str {
   push(@str, $src ? sprintf("[%08x]", $src) : '[]');
   join(' ',
     @str,
-    map { $_ ? "[$_]" : '[]' } ($msg->get_operator, $msg->get_group)
+    map { $_ ? "[$_]" : '[]' } (
+      $msg->get_operator || "<UNKNOWN>",
+      $msg->get_group    || "<UNKNOWN>"
+    )
   );
 }
 
@@ -312,7 +315,7 @@ sub _getter {
       my $fval = $unp->[$idx];
       if (!$fval || !@$fval) {
         $fval = [];
-        @$fval = $class->_class_msg->get_field_by_idx($idx);
+        @$fval = $class->_class_msg->get_field_vals_by_idx($idx);
       }
       if ($mapper) {
         @val = map { $mapper->($_, $class) } @$fval;
@@ -626,7 +629,7 @@ perl types for them:
 
 =head1 SEE ALSO
 
-L<Net::Nmsg::IO>, L<Net::Nmsg::Input>, L<Net::Nmsg::Output>, L<Net::WDNS>, L<nmsgtool(1)>
+L<Net::Nmsg>, L<Net::Nmsg::IO>, L<Net::Nmsg::Input>, L<Net::Nmsg::Output>, L<Net::WDNS>, L<nmsgtool(1)>
 
 =head1 AUTHOR
 
@@ -100,9 +100,20 @@ sub set_source         { shift->_set_xs_opt(source         => @_) }
 sub set_group          { shift->_set_xs_opt(group          => @_) }
 sub set_buffered       { shift->_set_xs_opt(buffered       => @_) }
 sub set_zlibout        { shift->_set_xs_opt(zlibout        => @_) }
-sub set_rate           { shift->_set_xs_opt(rate           => @_) }
 sub set_endline        { shift->_set_xs_opt(endline        => @_) }
 
+sub set_rate {
+  my $self = shift;
+  my($rate, $freq);
+  if (@_ == 1 && ref $_[0]) {
+    ($rate, $freq) = @{$_[0]};
+  }
+  else {
+    ($rate, $freq) = @_;
+  }
+  $self->_set_xs_opt(rate => $rate, $freq)
+}
+
 ###
 
 sub is_file { (shift->_xs || return)->is_file }
@@ -115,16 +126,16 @@ sub is_cb   { (shift->_xs || return)->is_cb   }
 sub _map_opts {
   my $self = shift;
   my %opt  = @_;
-  my $vendor  = delete $opt{filter_vendor};
-  my $msgtype = delete $opt{filter_msgtype};
-  return %opt unless defined $vendor || defined $msgtype;
-  $self->error("vendor and msgtype required") && return
-    unless defined $vendor && defined $msgtype;
-  $opt{filter_msgtype} = [$vendor, $msgtype];
-  #
   my $rate = delete $opt{rate};
   my $freq = delete $opt{freq};
   $opt{rate} = [$rate, $freq];
+  my $vendor  = delete $opt{filter_vendor};
+  my $msgtype = delete $opt{filter_msgtype};
+  if (defined $vendor || defined $msgtype) {
+    $self->error("vendor and msgtype required")
+      unless defined $vendor && defined $msgtype;
+    $opt{filter_msgtype} = [$vendor, $msgtype];
+  }
   %opt;
 }
 
@@ -397,7 +408,7 @@ Write the given Net::Nmsg::Msg object to this output.
 
 =head1 SEE ALSO
 
-L<Net::Nmsg::IO>, L<Net::Nmsg::Input>, L<Net::Nmsg::Msg>, L<nmsgtool(1)>
+L<Net::Nmsg>, L<Net::Nmsg::IO>, L<Net::Nmsg::Input>, L<Net::Nmsg::Msg>, L<nmsgtool(1)>
 
 =head1 AUTHOR
 
@@ -76,8 +76,8 @@ sub map_makers { \@From };
 
 sub _from_ip { $_[0] =~ /:/ ? ipv6_aton($_[0]) : inet_aton($_[0]) }
 
-$From[NMSG_FT_IP    ] = sub { \&_from_ip     };
-$From[NMSG_FT_ENUM  ] = sub {
+$From[NMSG_FT_IP  ] = sub { \&_from_ip     };
+$From[NMSG_FT_ENUM] = sub {
   my $idx = shift;
   sub {
     my($val, $class) = @_;
@@ -110,8 +110,8 @@ sub map_makers { \@To };
 
 sub _to_ip { length $_[0] > 4 ? ipv6_n2x($_[0]) : inet_ntoa($_[0]) }
 
-$To[NMSG_FT_IP    ] = sub { \&_to_ip     };
-$To[NMSG_FT_ENUM  ] = sub {
+$To[NMSG_FT_IP  ] = sub { \&_to_ip     };
+$To[NMSG_FT_ENUM] = sub {
   my $idx = shift;
   sub {
     my($val, $class) = @_;
@@ -671,7 +671,7 @@ As such, root privileges are likely necessary for them to be useful.
 
 =head1 SEE ALSO
 
-L<Net::Nmsg::IO>, L<Net::Nmsg::Input>, L<Net::Nmsg::Output>, L<nmsgtool(3)>
+L<Net::Nmsg>, L<Net::Nmsg::IO>, L<Net::Nmsg::Input>, L<Net::Nmsg::Output>, L<nmsgtool(3)>
 
 
 =head1 AUTHOR
@@ -61,7 +61,7 @@ use base qw( Exporter DynaLoader );
 sub dl_load_flags { 0x01 } # global option
 
 BEGIN {
-  $VERSION = '0.13';
+  $VERSION = '0.15';
   bootstrap Net::Nmsg $VERSION;
 }
 
@@ -1,10 +1,10 @@
 /*
  * perl_math_int64.c - This file is in the public domain
- * Author: Salvador Fandino <sfandino@yahoo.com>
+ * Author: "Salvador Fandino <sfandino@yahoo.com>, Dave Rolsky <autarch@urth.org>"
  *
- * Generated on: 2014-10-30 11:43:56
- * Math::Int64 version: 0.33
- * Module::CAPIMaker version: 0.02
+ * Generated on: 2015-03-09 17:03:24
+ * Math::Int64 version: 0.51
+ * Module::CAPIMaker version: 
  */
 
 #include "EXTERN.h"
@@ -1,11 +1,11 @@
 /*
  * perl_math_int64.h - This file is in the public domain
- * Author: Salvador Fandino <sfandino@yahoo.com>
+ * Author: "Salvador Fandino <sfandino@yahoo.com>, Dave Rolsky <autarch@urth.org>"
  * Version: 2.1
  *
- * Generated on: 2014-10-30 11:43:56
- * Math::Int64 version: 0.33
- * Module::CAPIMaker version: 0.02
+ * Generated on: 2015-03-09 17:03:24
+ * Math::Int64 version: 0.51
+ * Module::CAPIMaker version: 
  */
 
 #if !defined (PERL_MATH_INT64_H_INCLUDED)