subnets 1.0.0pre → 1.0.0pre1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ca7677ed446bf92038930c57e31d26878b05fe6dc7c6fa4cf5f731ec58a5340
4
- data.tar.gz: 3d82804a72f2181c5036d69aad617c927bac2eb64ee4fe496d822ca6a006a043
3
+ metadata.gz: 54f0c79b3c0859e0d9cd2758276652d48fd0ceeece739695691b76c5cb8d01a7
4
+ data.tar.gz: 69b64044d769ede44735081a0411b088cf3016bf6a4a732238c5996c105e5e19
5
5
  SHA512:
6
- metadata.gz: 1f152961d4118eac6bb2bfafb1f65bdc94ee971b6ff0215542790a446da7e3c3e09d35278dcbfbd8e918f5defad442dbf38d3cb067f39521a3e9fa67ae86d853
7
- data.tar.gz: 2655d78b2dbf09510a28cdabaea00c572c5038889b7724c34fa96e2f1bb72319831c468b57caf39055ddae8d106ff91a61e7695d9dd3762d6fbe92c7f7ac6f4b
6
+ metadata.gz: ceebcd95e1fb22ee86acf2c4eb606c3deda2a97c04a7f57d0e97f5493974441855f3e7a04e2525e6b4ff1a88f6508df29b548bebcfca36dceb0ee4384ab76f82
7
+ data.tar.gz: ac0e808b3e42227f53881eed50a807e136a0b2602f0402bda120a42f9cc78d16ce64029cd2d5aa2e6fd9456dfb797411fe5496c6fda5321528e4cd39a828e591
data/README.md CHANGED
@@ -27,11 +27,11 @@ better. Plotted on logscale. (Ruby 2.5.0p0, 2.5 GHz Intel Core i7).
27
27
  ```
28
28
  $ bundle exec rake benchmark TEST=test/private_networks_benchmark
29
29
 
30
- ipaddr : 46.25us/ip ██████████████████████████████████▋
31
- ipaddress : 63.88us/ip ██████████████████████████████████████▏
32
- netaddr : 31.03us/ip ██████████████████████████████▎
33
- *subnets : 4.19us/ip ████████▏
34
- rack (regexp) : 5.25us/ip ██████████▋
30
+ ipaddr : 46.25μs/ip ██████████████████████████████████▋
31
+ ipaddress : 63.88μs/ip ██████████████████████████████████████▏
32
+ netaddr : 31.03μs/ip ██████████████████████████████▎
33
+ *subnets : 4.19μs/ip ████████▏
34
+ rack (regexp) : 5.25μs/ip ██████████▋
35
35
  ' ' ' ' '
36
36
  2 5 10 20 50
37
37
  ```
data/ext/subnets/ext.c CHANGED
@@ -318,54 +318,45 @@ method_ip4_band(VALUE self, VALUE other) {
318
318
 
319
319
  VALUE
320
320
  method_ip6_not(VALUE self) {
321
- ip6_t *ip, not;
321
+ ip6_t *ip;
322
322
  Data_Get_Struct(self, ip6_t, ip);
323
- for (int i=0; i<8; i++) {
324
- not.x[i] = ~(ip->x[i]);
325
- }
326
- return ip6_new(IP6, not);
323
+ return ip6_new(IP6, ip6_not(*ip));
327
324
  }
328
325
 
329
326
  VALUE
330
327
  method_ip6_bor(VALUE self, VALUE other) {
331
- ip6_t *a, *b, bor;
328
+ ip6_t *a, *b;
332
329
 
333
330
  assert_kind_of(other, IP6);
334
331
 
335
332
  Data_Get_Struct(self, ip6_t, a);
336
333
  Data_Get_Struct(other, ip6_t, b);
337
- for (int i=0; i<8; i++) {
338
- bor.x[i] = a->x[i] | b->x[i];
339
- }
340
- return ip6_new(IP6, bor);
334
+
335
+ return ip6_new(IP6, ip6_bor(*a, *b));
341
336
  }
342
337
 
343
338
  VALUE
344
339
  method_ip6_xor(VALUE self, VALUE other) {
345
- ip6_t *a, *b, xor;
340
+ ip6_t *a, *b;
346
341
 
347
342
  assert_kind_of(other, IP6);
348
343
 
349
344
  Data_Get_Struct(self, ip6_t, a);
350
345
  Data_Get_Struct(other, ip6_t, b);
351
- for (int i=0; i<8; i++) {
352
- xor.x[i] = a->x[i] ^ b->x[i];
353
- }
354
- return ip6_new(IP6, xor);
346
+
347
+ return ip6_new(IP6, ip6_xor(*a, *b));
355
348
  }
356
349
 
357
350
  VALUE
358
351
  method_ip6_band(VALUE self, VALUE other) {
359
- ip6_t *a, *b, band;
352
+ ip6_t *a, *b;
360
353
 
361
354
  assert_kind_of(other, IP6);
362
355
 
363
356
  Data_Get_Struct(self, ip6_t, a);
364
357
  Data_Get_Struct(other, ip6_t, b);
365
- for (int i=0; i<8; i++) {
366
- band.x[i] = a->x[i] & b->x[i];
367
- }
368
- return ip6_new(IP6, band);
358
+
359
+ return ip6_new(IP6, ip6_band(*a, *b));
369
360
  }
370
361
 
371
362
  /**
@@ -546,6 +537,33 @@ method_net6_to_s(VALUE self) {
546
537
  return rb_str_new2(buf);
547
538
  }
548
539
 
540
+ VALUE
541
+ method_ip4_to_i(VALUE self) {
542
+ ip4_t *ip;
543
+ Data_Get_Struct(self, ip4_t, ip);
544
+ return RB_UINT2NUM(*ip);
545
+ }
546
+
547
+ VALUE
548
+ method_ip6_to_i(VALUE self) {
549
+ VALUE ret;
550
+ ip6_t *ip;
551
+ Data_Get_Struct(self, ip6_t, ip);
552
+
553
+ ID lshift = rb_intern("<<");
554
+ ID plus = rb_intern("+");
555
+
556
+ ret = RB_INT2NUM(0);
557
+
558
+ for (int i=0; i<8; i++) {
559
+ VALUE hextet = RB_UINT2NUM(ip->x[i]);
560
+ VALUE inc = rb_funcall(hextet, lshift, 1, RB_INT2NUM(16*(7-i)));
561
+ ret = rb_funcall(ret, plus, 1, inc);
562
+ }
563
+
564
+ return ret;
565
+ }
566
+
549
567
  /**
550
568
  * @return [Boolean]
551
569
  */
@@ -600,10 +618,7 @@ method_ip6_eql_p(VALUE self, VALUE other) {
600
618
  Data_Get_Struct(self, ip6_t, a);
601
619
  Data_Get_Struct(other, ip6_t, b);
602
620
 
603
- for (int i=0; i<8; i++) {
604
- if (a->x[i] != b->x[i]) return Qfalse;
605
- }
606
- return Qtrue;
621
+ return ip6_eql_p(*a, *b) ? Qtrue : Qfalse;
607
622
  }
608
623
 
609
624
  /**
@@ -745,34 +760,86 @@ method_net6_hextets(VALUE self) {
745
760
  return hextets;
746
761
  }
747
762
 
763
+ /**
764
+ * @return [Subnets::Net4] the smallest subnet that includes all of
765
+ * the subnets in +nets+
766
+ *
767
+ * @param nets [Array<Subnets::Net4>]
768
+ */
748
769
  VALUE
749
770
  method_net4_summarize(VALUE class, VALUE nets) {
750
771
  net4_t result;
751
772
 
752
- result.address = mk_mask4(32);
753
- result.prefixlen = 32;
754
- result.mask = mk_mask4(32);
755
-
756
773
  for (ssize_t i = 0; i < RARRAY_LEN(nets); i++) {
757
774
  VALUE rbnet = RARRAY_AREF(nets, i);
758
775
 
759
776
  assert_kind_of(rbnet, Net4);
760
777
 
761
- net4_t *net;
778
+ const net4_t *net;
762
779
  Data_Get_Struct(rbnet, net4_t, net);
763
780
 
764
- result.address &= (net->address & net->mask);
781
+ if (i == 0) {
782
+ result.address = (net->address & net->mask);
783
+ result.prefixlen = net->prefixlen;
784
+ result.mask = net->mask;
785
+ } else {
786
+ if (result.prefixlen > net->prefixlen) {
787
+ result.prefixlen = net->prefixlen;
788
+ result.mask = net->mask;
789
+ result.address &= result.mask;
790
+ }
765
791
 
766
- while (!net4_include_net4_p(result, *net)) {
767
- result.prefixlen -= 1;
768
- result.mask = mk_mask4(result.prefixlen);
769
- result.address &= result.mask;
792
+ while (result.address != (net->address & result.mask)) {
793
+ result.prefixlen -= 1;
794
+ result.mask = mk_mask4(result.prefixlen);
795
+ result.address &= result.mask;
796
+ }
770
797
  }
771
798
  }
772
799
 
773
800
  return net4_new(class, result);
774
801
  }
775
802
 
803
+ /**
804
+ * @return [Subnets::Net6] the smallest subnet that includes all of
805
+ * the subnets in +nets+
806
+ *
807
+ * @param nets [Array<Subnets::Net6>]
808
+ */
809
+ VALUE
810
+ method_net6_summarize(VALUE class, VALUE nets) {
811
+ net6_t result;
812
+
813
+ for (ssize_t i = 0; i < RARRAY_LEN(nets); i++) {
814
+ VALUE rbnet = RARRAY_AREF(nets, i);
815
+
816
+ assert_kind_of(rbnet, Net6);
817
+
818
+ net6_t *net;
819
+ Data_Get_Struct(rbnet, net6_t, net);
820
+
821
+ if (i == 0) {
822
+ result.address = ip6_band(net->address, net->mask);
823
+ result.prefixlen = net->prefixlen;
824
+ result.mask = net->mask;
825
+ } else {
826
+ if (result.prefixlen > net->prefixlen) {
827
+ result.prefixlen = net->prefixlen;
828
+ result.mask = net->mask;
829
+ result.address = ip6_band(result.address, result.mask);
830
+ }
831
+
832
+ while(!ip6_eql_p(result.address, ip6_band(net->address, result.mask))) {
833
+ result.prefixlen -= 1;
834
+ result.mask = mk_mask6(result.prefixlen);
835
+ result.address = ip6_band(result.address, result.mask);
836
+ }
837
+ }
838
+ }
839
+
840
+ return net6_new(class, result);
841
+ }
842
+
776
843
  /**
777
844
  * Try parsing +str+ as Net4, Net6, IP4, IP6.
778
845
  *
@@ -942,6 +1009,7 @@ void Init_Subnets() {
942
1009
  rb_define_alias(IP4, "eql?", "==");
943
1010
  rb_define_method(IP4, "hash", method_ip4_hash, 0);
944
1011
  rb_define_method(IP4, "to_s", method_ip4_to_s, 0);
1012
+ rb_define_method(IP4, "to_i", method_ip4_to_i, 0);
945
1013
 
946
1014
  rb_define_method(IP4, "~", method_ip4_not, 0);
947
1015
  rb_define_method(IP4, "|", method_ip4_bor, 1);
@@ -955,6 +1023,7 @@ void Init_Subnets() {
955
1023
  rb_define_alias(IP6, "eql?", "==");
956
1024
  rb_define_method(IP6, "hash", method_ip6_hash, 0);
957
1025
  rb_define_method(IP6, "to_s", method_ip6_to_s, 0);
1026
+ rb_define_method(IP6, "to_i", method_ip6_to_i, 0);
958
1027
  rb_define_method(IP6, "hextets", method_ip6_hextets, 0);
959
1028
 
960
1029
  rb_define_method(IP6, "~", method_ip6_not, 0);
@@ -988,6 +1057,7 @@ void Init_Subnets() {
988
1057
  rb_define_singleton_method(Net6, "parse", method_net6_parse, 1);
989
1058
  rb_define_singleton_method(Net6, "random", method_net6_random, -1);
990
1059
  rb_define_singleton_method(Net6, "new", method_net6_new, 2);
1060
+ rb_define_singleton_method(Net6, "summarize", method_net6_summarize, 1);
991
1061
  rb_define_method(Net6, "==", method_net6_eql_p, 1);
992
1062
  rb_define_alias(Net6, "eql?", "==");
993
1063
  rb_define_method(Net6, "hash", method_net6_hash, 0);
data/ext/subnets/ipaddr.c CHANGED
@@ -187,6 +187,50 @@ net6_snprint(net6_t net, char *str, size_t size) {
187
187
  return n0 + n1;
188
188
  }
189
189
 
190
+ int
191
+ ip6_eql_p(ip6_t a, ip6_t b) {
192
+ for (int i=0; i<8; i++) {
193
+ if (a.x[i] != b.x[i]) return 0;
194
+ }
195
+ return !0;
196
+ }
197
+
198
+ ip6_t
199
+ ip6_not(ip6_t ip) {
200
+ ip6_t not;
201
+ for (int i=0; i<8; i++) {
202
+ not.x[i] = ~(ip.x[i]);
203
+ }
204
+ return not;
205
+ }
206
+
207
+ ip6_t
208
+ ip6_band(ip6_t a, ip6_t b) {
209
+ ip6_t band;
210
+ for (int i=0; i<8; i++) {
211
+ band.x[i] = a.x[i] & b.x[i];
212
+ }
213
+ return band;
214
+ }
215
+
216
+ ip6_t
217
+ ip6_bor(ip6_t a, ip6_t b) {
218
+ ip6_t bor;
219
+ for (int i=0; i<8; i++) {
220
+ bor.x[i] = a.x[i] | b.x[i];
221
+ }
222
+ return bor;
223
+ }
224
+
225
+ ip6_t
226
+ ip6_xor(ip6_t a, ip6_t b) {
227
+ ip6_t xor;
228
+ for (int i=0; i<8; i++) {
229
+ xor.x[i] = a.x[i] ^ b.x[i];
230
+ }
231
+ return xor;
232
+ }
233
+
190
234
  int
191
235
  hexvalue(unsigned int c) {
192
236
  if (c-'0'<10) return c-'0';
data/ext/subnets/ipaddr.h CHANGED
@@ -102,4 +102,10 @@ size_t read_ip6_strict(const char *, ip6_t *);
102
102
  size_t read_net4_strict(const char *, net4_t *);
103
103
  size_t read_net6_strict(const char *, net6_t *);
104
104
 
105
+ int ip6_eql_p(ip6_t, ip6_t);
106
+ ip6_t ip6_not(ip6_t);
107
+ ip6_t ip6_band(ip6_t, ip6_t);
108
+ ip6_t ip6_bor(ip6_t, ip6_t);
109
+ ip6_t ip6_xor(ip6_t, ip6_t);
110
+
105
111
  #endif /* __IPADDR_H__ */
@@ -43,11 +43,11 @@ puts "# check if single IP is in the private IPv4 subnets"
43
43
  }.total
44
44
  count += 100
45
45
  end
46
- puts "%10.10s: checked %8d (%6d hits, %2d%%) in %2.2f for %7.2fus/ip" %
46
+ puts "%10.10s: checked %8d (%6d hits, %2d%%) in %2.2f for %7.2fμs/ip" %
47
47
  [check.name, count, hits, 100.0*hits/count, total, total/count*1e6]
48
48
 
49
49
  results[check.name] = total/count*1e6
50
50
  end
51
51
 
52
52
  puts
53
- plotbarslogscale(prefix: '%-15.15s: %5.2fus/ip ', width: 46, min: 2, max: 65, tics: [2,5,10,20,50], data: results)
53
+ plotbarslogscale(prefix: '%-15.15s: %5.2fμs/ip ', width: 46, min: 2, max: 65, tics: [2,5,10,20,50], data: results)
@@ -3,6 +3,7 @@ require 'test_helper'
3
3
  module Subnets
4
4
  class TestNet4 < Minitest::Test
5
5
  include EqlAndHash
6
+ include Summarize
6
7
 
7
8
  def klass
8
9
  Net4
@@ -70,5 +71,20 @@ module Subnets
70
71
  end
71
72
  end
72
73
  end
74
+
75
+ def test_summarize
76
+ data = {
77
+ '192.168.0.0/24' => ['192.168.0.0/25', '192.168.0.128/25'],
78
+ '10.0.0.0/8' => ['10.0.0.0/24', '10.250.2.3/19'],
79
+ }
80
+
81
+ data.each do |summ, nets|
82
+ summ = Subnets.parse(summ)
83
+ nets = nets.map(&Subnets.method(:parse))
84
+
85
+ assert_equal summ, Net4.summarize(nets)
86
+ assert_summarizes summ, nets
87
+ end
88
+ end
73
89
  end
74
90
  end
@@ -4,6 +4,7 @@ require 'ipaddr'
4
4
  module Subnets
5
5
  class TestNet6 < Minitest::Test
6
6
  include EqlAndHash
7
+ include Summarize
7
8
 
8
9
  # for EqlAndHash
9
10
  def klass
data/test/summarize.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Summarize
2
+ def test_summarizing_net_includes_subnets
3
+ random = Random.new
4
+ start = Time.now
5
+ until Time.now - start > TIMED_TEST_DURATION
6
+ nets = (1..3).map{klass.random(random)}
7
+ summ = klass.summarize(nets)
8
+ assert_operator summ.prefixlen, :>=, 0, "summarized #{nets} to invalid #{summ}"
9
+ assert_summarizes klass.summarize(nets), nets
10
+ end
11
+ end
12
+ end
data/test/test_helper.rb CHANGED
@@ -18,6 +18,7 @@ Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new,
18
18
  require 'subnets'
19
19
  require 'well_known_subnets'
20
20
  require 'eql_and_hash'
21
+ require 'summarize'
21
22
 
22
23
  TIMED_TEST_DURATION = (ENV['TIMED_TEST_DURATION'] || 1).to_i
23
24
 
@@ -28,3 +29,14 @@ end
28
29
  def refute_include(obj, val)
29
30
  refute obj.include?(val), "#{obj} should not include #{val}"
30
31
  end
32
+
33
+ def assert_summarizes(summ, nets)
34
+ bad = nil
35
+ nets.each do |net|
36
+ unless summ.include?(net)
37
+ bad = net
38
+ break
39
+ end
40
+ end
41
+ refute bad, "#{nets} not summarized by #{summ}: #{summ}.include?(#{bad}) was false"
42
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: subnets
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0pre
4
+ version: 1.0.0pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Mahoney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-12 00:00:00.000000000 Z
11
+ date: 2018-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -175,6 +175,7 @@ files:
175
175
  - test/subnets/net4_test.rb
176
176
  - test/subnets/net6_test.rb
177
177
  - test/subnets_test.rb
178
+ - test/summarize.rb
178
179
  - test/test_helper.rb
179
180
  - test/well_known_subnets.rb
180
181
  homepage: https://github.com/raisemarketplace/subnets