Skip to content

Instantly share code, notes, and snippets.

@markusl
Created July 12, 2011 17:12
Show Gist options
  • Save markusl/1078445 to your computer and use it in GitHub Desktop.
Save markusl/1078445 to your computer and use it in GitHub Desktop.
Slow F# API for IpToCountry.csv - Class for mapping IP addresses to countries in FSharp using GPL'd CSV database from http://software77.net/geo-ip/
module IpToCountry
open System
open System.IO
/// Construct new class for mapping ip addresses to countries using
/// database from http://software77.net/geo-ip/
type IpToCountrySlow(?fileName) =
let fileName = defaultArg fileName "IpToCountry.csv"
// Read all non-comment lines
let ipToCountryLines = File.ReadAllLines(fileName) |> Seq.filter (fun line -> not(line.StartsWith("#")))
// File format:
// "1464729600","1464860671","ripencc","1117497600","DE","DEU","Germany"
let parseSingleLine (line : string) =
let lineSplit = line.Replace("\"", "").Split(',')
let country = lineSplit.[6]
let ipStart = Convert.ToUInt32(lineSplit.[0])
let ipEnd = Convert.ToUInt32(lineSplit.[1])
country, (ipStart, ipEnd)
// map to format useful for further processing
let countryIpList = ipToCountryLines |> Seq.map parseSingleLine |> List.ofSeq
// converts a human-readable ipv4 address to integer
let ipToInteger (ip : string) =
let ipParts = ip.Split('.') |> Array.map (fun byte -> Byte.Parse(byte))
let ipNumbers = ipParts |> Array.mapi (fun i part -> ((uint32)part <<< ((3-i)*8)))
ipNumbers |> Array.sum
let entryContainsTheIp integerIp (country, (ipStart, ipEnd)) =
ipStart <= integerIp && ipEnd >= integerIp
/// Map an ipv4 address to country or throw KeyNotFoundException or
/// IndexOutOfRangeException in case of an error
member this.getCountry ip =
let integerIp = ipToInteger ip
let countryIpMap = countryIpList |> List.find (entryContainsTheIp integerIp)
fst countryIpMap
// Example usage:
open IpToCountry
let testIps = ["205.188.215.229"; "74.55.102.12"; "94.245.116.7"; "158.127.18.60"; "192.71.238.76"; "194.106.111.42"]
let ipToCountry = IpToCountrySlow()
testIps |> List.map ipToCountry.getCountry |> List.iter (printfn "%s")
(* Outputs:
United States
United States
United Kingdom
European Union
Sweden
Estonia *)
@markusl
Copy link
Author

markusl commented Jul 13, 2011

This demonstrates the simplest possible implementation, which works ok for usual case, but it quite slow for bigger amounts of data. Faster version that speeds up IP lookups by storing them into separate buckets can be found at https://gist.github.com/1080544

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment