Re: A ChatGPT conversation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Dec 20, 2023 at 08:42:02PM +0000, Dave Cridland wrote:

> Half the battle is, of course, being able to read the result and decide if
> it's actually a valid approach - but there are tools to assist with that
> too.

Yes, and gets a quite a bit more dicey with less popular languages.  The
same question for Haskell gave me, rather unworkable code, which
attempts to do too much of the work "by hand", rather than use existing
libraries:

    import Data.ByteString (ByteString)
    import Data.List (intercalate)
    import Data.Word (Word8, Word16)
    import Network.Socket (inet_pton)       -- [V] No such interface.
                                            -- Wouldn't return a family if it existed.
                                            -- Also, no attempt to import inet_ntoa.

    -- Function to validate and normalize an IP address
    normalizeIP :: ByteString -> Maybe ByteString
    normalizeIP addr = do
        family <- inet_pton addr
        case family of
            -- IPv4: Normalize by converting to a string and back
            AF_INET -> Just $ ByteString.pack $ show (inet_ntoa family)
            -- IPv6: Expand zero compressions and lowercase hex digits
            AF_INET6 -> Just $ ByteString.concat $ map (ByteString.pack . showHex) segments
            where       -- Syntax error: insufficient indentation
                -- [V] This is no way to split 16 bytes into 8 Word16 groups.
                segments = expandZeros $ map Word16 $ ByteString.unpack family

                -- [V] This function is nonsense.
                expandZeros :: [Word16] -> [[Word16]]
                expandZeros (0:xs) = [0] : expandZeros xs
                expandZeros (x:xs) = [x] : expandZeros xs
                expandZeros [] = []

                -- [V] This is broken, also, there's a correct `showHex` in "Numeric"
                -- Convert a Word16 to lowercase hex string
                showHex :: Word16 -> String
                showHex w = toLower . showHex' $ w
                showHex' :: Word16 -> String
                showHex' w = concat $ map (\n -> showHexDigit $ fromIntegral (w `shiftR` (4 * n))) [0..3]

                - [V] This only works for decimal digits
                -- Convert a single nibble to a hex digit
                showHexDigit :: Word8 -> Char
                showHexDigit n = chr $ 48 + fromIntegral n
            _ -> Nothing

An much more natural version (be it one that tolerates leading
whitespace, but does not tolerate excess leading 0's in IPv4 quads)
would be:

    import qualified Data.ByteString.Builder as B
    import qualified Data.ByteString.Builder.Extra as B
    import qualified Data.ByteString.Char8 as B
    import qualified Data.ByteString.Lazy as L
    import qualified Data.IP as IP
    import qualified Data.IP.Builder as IP

    normalizeIP :: B.ByteString -> (Maybe B.ByteString)
    normalizeIP addr = case (reads (B.unpack addr)) of
        [(ip, "")] -> build ip
        _          -> Nothing
      where
        build (IP.IPv4 ip) = sized 16 $ IP.ipv4Builder ip
        build (IP.IPv6 ip) = sized 39 $ IP.ipv6Builder ip
        sized sz bldr = Just $! L.toStrict $ tolazy bldr
          where
            tolazy = B.toLazyByteStringWith (B.untrimmedStrategy sz sz) L.empty

I shared the above solution with Bard, which then however gave sensible
rationale notes for the essential features.  It does feel a bit odd to
step up to train the AI for free...

A more precise version via getaddrinfo(3) that handles more exotic IPv4
addresses and does not accept leading whitespace:

    import qualified Data.ByteString.Builder as B
    import qualified Data.ByteString.Builder.Extra as B
    import qualified Data.ByteString.Char8 as B
    import qualified Data.ByteString.Lazy as L
    import qualified Data.IP as IP
    import qualified Data.IP.Builder as IP
    import Network.Socket ( AddrInfo(..), AddrInfoFlag(..), Family(..), SockAddr(..)
                          , SocketType(..), defaultHints, getAddrInfo )

    -- Function to validate and normalize an IP address
    normalizeIP :: B.ByteString -> IO (Maybe B.ByteString)
    normalizeIP addr = do
        ais <- getAddrInfo numeric (Just (B.unpack addr)) Nothing
        case addrAddress <$> ais of
            [SockAddrInet  _   sin_addr]    -> hostAddr4 sin_addr
            [SockAddrInet6 _ _ sin6_addr _] -> hostAddr6 sin6_addr
            _                               -> pure Nothing
      where
        numeric = Just defaultHints { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV]
                                    , addrSocketType = Datagram }
        hostAddr4 = sized 16 . IP.ipv4Builder . IP.fromHostAddress
        hostAddr6 = sized 39 . IP.ipv6Builder . IP.fromHostAddress6
        sized sz bldr = pure $ Just $! L.toStrict $ norm
          where
            norm = B.toLazyByteStringWith (B.untrimmedStrategy sz sz) L.empty bldr

-- 
    Viktor.




[Index of Archives]     [IETF Annoucements]     [IETF]     [IP Storage]     [Yosemite News]     [Linux SCTP]     [Linux Newbies]     [Mhonarc]     [Fedora Users]

  Powered by Linux