views:

209

answers:

3

Given input of 0 to 32, representing the number of one-bits in an IP4 network mask (corresponding to a CIDR block size as in /19), what's

  1. An elegant way to turn that into a four-byte long net mask
  2. A fast way way to turn that into a four-byte long net mask

Prototype:

Function NetMaskFromBitCount(BitCount As Long) As Long
   'Logic here '
End Function

Notice this is complicated by the fact that VB6 doesn't do unsigned, so regular math tricks often don't work. If (Number And &H80000000) is nonzero, then Number \ 2 will NOT be the same as a SHR operation.

I've come up with a few ways, but I don't think they're elegant, and they're probably not that fast.

One idea I had is to strategically use the CopyMemory API, which is VERY fast. I've solved some signed/unsigned Long problems in the past by just blatting the Long into a Byte(0 To 3) and working with each portion as needed.

Since I'm also working with the inet_ntoa() and inet_addr() Windows API functions and they return the IP serial number in reverse byte order, a solution that returns the bytes in reverse order is great (I already have a function to flip the byte order if needed, but avoiding it would be nice, too).

Examples:

Input = 2
Output = -1073741824 (&HC0000000)
Alternate Output = 12 (&HC0, reverse byte order)

Input = 19
Output = -8192  (&HFFFFE000)
Alternate Output = 14745599 (&H00E0FFFF, reverse byte order)

Working solutions are good, but ELEGANT or FAST is what I'm looking for.

+1  A: 
Function NetMaskFromBitCount(ByVal lBitCount As Long) As Long
    If lBitCount > 0 Then
        NetMaskFromBitCount = -1 * 2 ^ (32 - lBitCount)
    End If
End Function

Sorry for the hungarian :-)) Had to make this param ByVal!

And the test goes here:

Debug.Assert NetMaskFromBitCount(19) = &HFFFFE000
Debug.Assert NetMaskFromBitCount(2) = &HC0000000
Debug.Assert NetMaskFromBitCount(32) = &HFFFFFFFF
Debug.Assert NetMaskFromBitCount(0) = 0
wqw
Ooh. I kept thinking of the first bit as a problem. But this solution observes that all the masks are negative (have the first bit set) except for input 0, and are in fact the negative powers of two. Very interesting. I should have listed all the values and I would have instantly seen this! Maybe not so fast—if I recall, the power operator in VB isn't the fastest in the world. But very simple and elegant!
Emtucifor
+1  A: 

You only have 32 possible inputs, so my guess is the fastest solution will be a lookup table referencing all 32 outputs from an array. It won't win points for elegance. Warning: air code

Function DoIt(ByVal Input As Long) As Long  
  Static lArray() As Long  
  Static bInitialised As Boolean   
  If Not bInitialised Then   
    ReDim Preserve lArray(0 To 31)   
    lArray(0) = 0   
    lArray(1) = &H80000000    
    lArray(2) = &HC0000000    
    ' etc... '  
    bInitialised = True 
  End If
  DoIt = lArray(Input)  ' for bonus marks raises an error on illegal input ' 
End Function  

If you want something more general, VBSpeed has a long-standing open competition for fast VB6 left-shifts.

  • Here's the current winner ShiftLeft04 which uses some horrendous, sorry I mean brilliant, tricks to get inline assembler in VB6. You'll laugh, you'll cry, you'll scream in terror...
  • The current runner-up ShiftLeft06 is about 200 lines of all-native VB6. Takes 1.3 times longer, but still quick.
MarkJ
Emtucifor
Emtucifor
@Emtucifor, Edited my answer to cover both those points.
MarkJ
A: 

I don't have VB6, but here is how I would do it in .Net

    Const allBroadcast As Integer = Integer.MaxValue ' = 2,147,483,647
    Dim mask As Integer
    'three examples
    mask = allBroadcast << (32 - 24) '/24
    mask = allBroadcast << (32 - 16) '/16
    mask = allBroadcast << (32 - 8) '/8
dbasnett
Yeah, I'm not too worried about doing it in .Net since it has unsigned data types and SHL/SHR.
Emtucifor