Monday, April 1, 2019

Rust language - WASM - WebGL - Game Of Life demo

Today I would shortly describe my first journey to Rust language. I combined multiple examples from Rust WASM documentation, WebGL. Whole code is here it was fun.

The implementation of Game Of Life is in functional style. The previous generation is list of cell coordinates. I learned about Rust itertools


const OFFSETS_AROUND: [[i32; 2]; 9] = [
  [-1, -1],[0, -1],[1, -1],
  [-1,  0],[0,  0],[1,  0],
  [-1,  1],[0,  1],[1,  1],
];

pub fn cells_around(current: &Cell) -> BoolCells {
    OFFSETS_AROUND.iter()
        .map(|o| {
            (
                o[0] == 0 && o[1] == 0,
                Cell {
                    x: (current.x + o[0]),
                    y: (current.y + o[1]),
                },
            )
        })
        .collect::<BoolCells>()
}

fn should_live<I>(group: I) -> bool where I: IntoIterator<Item = BoolCell> {
    let (was_alive, count) = group.into_iter()
            .fold((false, 0), |acc, candidate| match candidate.0 {
                true => (true, acc.1),
                false => (acc.0, acc.1 + 1),
            });
    count == 3 || (count == 2 && was_alive)
}

pub fn next_gen(generation: &Cells) -> Cells {
    let candidates_with_duplicates = generation.iter()
        .map(cells_around)
        .flatten();
    
    let grouped_by_location = candidates_with_duplicates
        .sorted_by(|candidate_a, candidate_b| candidate_a.1.cmp(&candidate_b.1))
        .group_by(|candidate| candidate.1.clone());
    
    grouped_by_location.into_iter()
        .map(|(key, group)| {
            let alive = should_live(group.into_iter());
            let cell =key.clone();
            (alive, cell)
        })
        .filter(|c| c.0)
        .map(|c| c.1)
        .collect::<Cells>()
}

I stolen most of my WebGl code from this demo. I learned how to move code to modules and how to work with namespaces. For each live cell I tell WebGL to render 2 triangles. There is Z axis of the vertice, which I do not use, but have to pass it. So in total I'm passing single array of float[18*live-cells] to WebGL for rendering in single call. I guess it's much faster than using canvas API with call per cell.

All is integrated into WASM library all the plumbing is again stolen from wasm-bindgen documentation.

Here is the demo. If it doesn't render for you, check your adblock settings, maybe it's blocking all .wasm files to prevent crypto-currerncy mining.

Wednesday, September 24, 2014

jni4net 0.8.8 - release

Changes

  • [#35] - fixed - Integer overflow on 64-bit OS [by Geert Claeys]
  • reworked contributed code to become full owner of the code and be able to change license.
  • changed runtime license to MIT. Tools still GPLv3.
  • moved to github and jni4net.github.io
  • changed documentation to MarkDown and improved a bit. Links to new issue tracker and stackoverflow.
  • removed support for publishing jni4net to maven repository on google code.
  • Hope that community would enjoy the freedoms of MIT license and github and would fork it and contribute back :-)

    Download here

    Wednesday, March 7, 2012

    Unit of work using lambdas

    Are you tired of writing into log file on every begin and end of the method ? Use AOP. Well, not yet ready for post-compiler magic or dynamic runtime proxies ? Let's try lambdas (again).

    static void Main(string[] args)
    {
        Scope<LoggingFrame>.Wrap(() =>
        {
            Console.WriteLine("Hello world");
        });
    }

    Would produce this console
    2012-03-07 00:46:58,248 DEBUG - BEGIN Main
    Hello world
    2012-03-07 00:46:58,258 DEBUG - END   Main
    

    How to implement that ?
    [DebuggerStepThrough]
    public class Scope<TAdvice> where TAdvice : IAdvice, new()
    {
        public static void Wrap(Action body)
        {
            IAdvice advice = Activator.CreateInstance<TAdvice>();
            advice.OnEntry(body);
            try
            {
                body();
                advice.OnLeave(body);
            }
            catch (Exception ex)
            {
                advice.OnException(body, ex);
                throw;
            }
            finally
            {
                advice.OnFinally(body);
            }
        }
    }
    public interface IAdvice
    {
        void OnEntry(Delegate body);
        void OnLeave(Delegate body);
        void OnException(Delegate body, Exception exception);
        void OnFinally(Delegate body);
    }
    
    Implementation of the LoggingFrame advice is trivial.

    Note that you could get similar behavior with IDisposable and using keyword, but you would not be able to log pending exception.
    You could also think about TransactionScope, which would call tx.Complete() automatically when no exception is thrown.
    Further improvement is to use dependency injection to instantiate the advises.

    Another use-case is to implement Unit of Work or session/call context, while using TSL to reach topmost frame. I used it for NHibernate Session and EF DbContext (unit of work) recently. Interesting related article here.

    private void Main(string[] args)
    {
        Scope<UnitOfWork>.Wrap(session =>
        {
            var people = session.Person
                .Where(person => person.FirstName == "Pavel")
                .ToList();
    
            NestedLogic(people);
    
            //DbContext.SaveChanges() will be called here
        });
    
    }
    
    private void NestedLogic(IList<Person> people)
    {
        //this will lookup parent session in TSL and reuse it
        Scope<UnitOfWork>.Wrap(session =>
        {
            foreach (var person in people)
            {
                if (person.LastName == "Savara")
                {
                    person.Coder = true;
                }
                else
                {
                    session.Person.Remove(person);
                }
            }
        });
    }

    Composition of scopes could be beautified.
    Scope<LoggingFrame, TransactionFrame>.Wrap(() =>
    {
        throw new Exception("rollback please");
    });
    
    
    [DebuggerStepThrough]
    public class Scope<TOuterAdvice, TInnerAdvice> 
      where TOuterAdvice : IAdvice, new() 
      where TInnerAdvice : IAdvice, new()
    {
        public static void Wrap(Action body)
        {
            Scope<TOuterAdvice>.Wrap(()=> Scope<TInnerAdvice>.Wrap(body));
        }
    }
    

    All code in the article is simplified and real implementation is exercise for readers.

    Enjoy :-)

    Saturday, December 31, 2011

    Fluentator - generate fluent API for your structures

    When working with nested structures like configuration or XML it is bit of pain with syntax in C#. Consider this code below. The object initializes in C# 3.0 helped a lot, but it's still pretty far from ideal. Important point here is readability, which is achieved thru nesting the initializers.
    var model = new Model();
    var pavel = new Employee("Pavel");
    model.Companies.Add(new Company("Boldbrick & co.")
    {
        Departments = new List<Department>
        {
            new Department("Software & Visions", "swv")
            {
                Teams = new List<Team>
                {
                    new Team("Visions")
                    {
                        Employees = new List<Employee>
                        {
                            // I was forced to move 
                            // pavel variable declaration completely out of scope
                            pavel,
                            new Employee("Ondra"),
                        },
                        IsAwesome = true,
                    },
                    new Team("Developers")
                    {
                        Employees = new List<Employee>
                        (
                            // I can't do any statements or declarations here
                            // to prepare my data in-place
                            devNames.Select(n=>new Employee(n))
                        )
                        {
                            // note I can't add pavel first
                            pavel,
                        }
                    }
                }
            }
        }
    });
    
    But there are downsides with approach above. You can't easily add same instance into 2 nodes. It forces you to declare pavel variable completely out of scope. And you can't use statements to prepare your data in place either. Note how Pavel is inserted after other employees into Developers team. The LINQ Select() helped great deal here, but it's not always possible to use it. With more complex model and bigger tree to build, this will become unmanageable mess.

    So, extension methods and lambdas to rescue. Do you like code below better ? I certainly do. I can use statements and variable declarations in inner scope. I get more dense and readable code.

    var model = new Model();
    model.AddCompany("Boldbrick & co.", bb =>
    {
        bb.AddDepartment("Software & Visions", "swv", swv =>
        {
            // variable is still bit out of scope, but not in the root scope
            var pavel = new Employee("Pavel");
            swv.AddTeam("Visions", visions =>
            {
                visions.AddEmployee(pavel);
                visions.AddEmployee("Ondra");
                visions.IsAwesome = true;
            });
            swv.AddTeam("Developers", devs => 
            {
                devs.AddEmployee(pavel);
                // I can add more employees after Pavel
                devs.AddEmployees(devNames.Select(n => new Employee(n)), dev=>
                {
                    dev.Age = 33;
                });
                // and also can use any complex statement in-place
                for (int i = 0; i < devNames.Count; i++)
                {
                    int ix=i;
                    devs.AddEmployee(devNames[i], dev =>
                    {
                        dev.Age = ix;
                    });
                }
            });
        });
    );});

    How the extension method looks like ?

    Below is extension method over external structure Department, which accepts same parameters as Team constructor. Inside is instance creations and adding to the collection. Finally to allow the nesting of scopes, we pass the new instance to Action<> delegate.
    static public Team AddTeam(this Department self, string name, Action<Team> result = null)
    {
        var item = new Team(name);
        self.Teams.Add(item);
        if (result != null) result(item);
        return item;
    }
    

    Generate the extensions

    The extension method above is nice and useful but it's quite boring to write it for each combination of container and child item. Multiplied by all constructor signatures. So I decided to create ReflectionFluentator which can generate the code for you. It reads your model via reflection and generates the C# extensions. See sample how to use the generator.

    And the same thing for XML ? Sure. You provide the XSD to XsdFluentator. See sample how to use the generator.

    var doc=new XDocument();
    doc.AddLibrary("Prague",prague =>
    {
        prague.AddBook("Saturnin", book =>
        {
            book.AddAuthor("Zdenek Jirotka");
        });
        prague.AddBook("Bylo Nas 5", book =>
        {
            book.AddAuthor("Karel Polacek");
        });
    });
    
    This code can generate this XML as expected.
    <library id="Prague"
      xmlns="http://polyglottos.googlecode.com/svn/trunk/demomodel/library.xsd">
      <book name="Saturnin">
        <author name="Zdenek Jirotka" />
      </book>
      <book name="Bylo Nas 5">
        <author name="Karel Polacek" />
      </book>
    </library>
    
    Note that both generators are more prototypes than ready to ship. They don't handle any edge scenarios when reading the metadata or writing the code. Fluentator is part of Polyglottos project. If you like the idea and wish to contribute improvements, please talk back.

    At this point some of you may wonder what else could be made fluent this way. In my case, I realized that I need to generate the code and the code is just nested structure. So I created CodeDom code generator Polyglottos with fluent API. That's for another article next year. Enjoy the party tonight!

    Monday, September 19, 2011

    jni4net 0.8.6 - release

    Changes

    • fixed permission demand for sandboxed environments
    • improved proxygen can't find class reporting
    • improved the way we are looking for jni4net.j-xxx-.jar (while installed in GAC)
    • [#27] - fixed build script problems
    Download here

    Road map

    May people asked for road map. This is my vision, not a promise.

    Wednesday, August 17, 2011

    jni4net 0.8.5 - release

    Changes

    • [# 9] - support for indexer properties [by Johannes Rudolph]
    • [#22] - Potential field name clash due to increasing numbering strategy of field names
    • [#24] - Change of current directory during init may disrupt other code running in parallel
    • [#25] - DirectByteBuffer doesn't work with Java 7
    Download here

    Monday, July 18, 2011

    Host your own Roborumble server

    There are several reasons why you might want to run your own Roborumble server. Maybe you would like to run small local contest at your school or workplace. Or maybe you are testing your new shiny robot. Or may you want to collect some battle data, like myself.

    The current Roborumble server is created and maintained by Darkcanuck. Luckily he shares the RumbleServer sources via subversion. It is implemented in PHP and MySQL.

    Because I'm not PHP+MySQL expert, easiest solution for me was to download ready-made LAMP virtual machine and configure it.
    I know you are busy ;-), so I published my virtual appliance, you could just start using it. You can download it and run it in VMWare Player. Password is robocode, you should change it as soon. Or you could follow the guide below.

    Configure server

    Once you have LAMP stack up and running you need few things:
    Install subversion client
    Get PHP sources
    Configure RumbleServer
    Turn on URL rewriting mod in Apache
    Configure MySQL database and grant permissions

    The appliance has could be configured from web admin. You can access it on https://192.168.1.12/ where 192.168.1.12 is the IP address assigned to the virtual machine by your DHCP server. I will use 192.168.1.12 which was assigned to me in rest of the article.


    Webmin->Global configuration
      ->Configure Apache Modules->rewrite: switch ON

    MyPhpAdmin
      Create database->roborumble   Import script schema/schema.sql
      Priviledges->add new user->
        name->rumbleuser
        pass->rumblepass
        Database for user->None
        Global privileges->Select+Update+Insert+Delete

    On root shell do something like this
    apt-get install subversion
    cd /var/www/
    mkdir rumble
    cd rumble
    svn co http://darkcanuck.net/svn/rumbleserver/trunk/ .
    chmod g+w templates_c/
    chown :www-data templates_c/
    cat > participants.txt
    [paste participants.txt file and press Ctrl-D]
    cd config
    cp config.php-sample config.php
    


    Participants

    Are defined as list of robots and their .jar files. Big competition is driven by this file. We could simplify our own list like this. We host the file on the appliance in folder /var/www/. So create you own shortlist and update it there. Note there are starting and ending tags.
    ----
    <pre>
    jk.mega.DrussGT 2.0.4,http://www.minifly.rchomepage.com/robocode/jk.mega.DrussGT_2.0.4.jar
    voidious.Diamond 1.5.34b,http://www.dijitari.com/void/robocode/voidious.Diamond_1.5.34b.jar
    </pre>
    ----
    


    Configure client

    Download and install Robocode 1.7.3
    Edit c:\robocode\roborumble\roborumble.txt

      PARTICIPANTSURL=http://192.168.1.12/participants.txt
      UPDATEBOTSURL=http://192.168.1.12/rumble/RemoveOldParticipant
      RESULTSURL=http://192.168.1.12/rumble/UploadedResults
      RATINGS.URL=http://192.168.1.12/rumble/RatingsFile
      ITERATE=YES

    start c:\robocode\roborumble.bat

    Purge data

    If you need to purge the data from the roborumble database and start from scratch just delete rows using MyPhpAdmin.
    delete from battle_results;
    delete from game_pairings;
    delete from bot_data;
    delete from participants;
    delete from upload_stats;
    delete from upload_users;
    


    Oh, and once you are done with your local competition, don't forget to redirect back to official RoboRumble server and contribute your CPU power!


    Download turnkey-roborumble.2011-07-18.zip.torrent

    Thursday, July 7, 2011

    HowTo: Pass build property from TeamCity into Freemaker email notification template

    There is no magic, once you know it. My use-case was to prefix build number with configurable text before sending an email. The text was represented as TeamCity property system.tc.branch.number in my case.

    TeamCity email templates are implemented with Freemaker. The TeamCity exposes all build properties as hash table on SBuild.getBuildOwnParameters().

    In the template the instance of SBuild is accessible under build variable. Freemaker allows calling bean properties, in our case it would be build.buildOwnParameters, map lookup thru square-bracket syntax [] and literals are enclosed in apostrophes.

    #${build.buildOwnParameters['system.tc.branch.number']}.${build.buildNumber}
    

    Tuesday, June 28, 2011

    jni4net 0.8.4 - release

    Changes

    • Added ability to load proxies into specified classLoader by Jose Chillan.
      Bridge.LoadAndRegisterAssemblyFromClassLoader(File, ClassLoader)
    • JNIEnv.DetachCurrentThread() contributed by Renier B.
    • Bridge.setClrVersion() made static by Leonid Bogdanov
    Download here

    Saturday, March 26, 2011

    Raising Property Changed fast and safe - yet another solution

    This Thursday we discussed with my colleagues how to raise property changed event. We learned that it could be done
    a) via string literal,
    b) via lambda expression tree,
    c) via StackTrace or MethodBase.GetCurrentMethod().
    Nice summary is here. or here

    We did some naive performance test and learned that for 500k iterations on simple property, there is big difference in speed.
    a) is really fast, 13ms
    b) is slow, 1417ms, I guess because security checks apply for every call and every stack frame, and can't be fired from other properties.
    c) is slow, 2518ms, very slow, because expression tree is constructed for every call

    So we played with it for a while and I invented solution with anonymous type (at botom).

    Update 27.3.2011: I was so focused on speed that I didn't realized that the solution yesterday didn't work nicely for rename. What a shame. I hope people give me another chance today. Here we have postponed creation of the expression tree, it takes about 75ms. And this time my ReSharper renames it correctly :-)
    Update 5.12.2011: As Alex noted in comments, there was thread safety issue with Dictionary. The code is now fixed with Hashtable instead.


    public abstract class ModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private static readonly Hashtable Names = new Hashtable();
    
        protected void RaisePropertyChange(Func<string> key)
        {
            if (PropertyChanged != null)
            {
                var propertyName = (PropertyChangedEventArgs)Names[key];
                if (propertyName==null)
                {
                    propertyName = new PropertyChangedEventArgs(key());
                    lock (Names)
                    {
                        Names[key] = propertyName;
                    }
                }
                PropertyChanged(this, propertyName);
            }
        }
    
        protected static string Reg<T>(Expression<Func<T>> property)
        {
            return (property.Body as MemberExpression).Member.Name;
        }
    }
    
    public class Model : ModelBase
    {
        private string firstName2;
        public string FirstName2
        {
            get { return firstName2; }
            set
            {
                firstName2 = value;
                RaisePropertyChange(() => Reg(() => FirstName2));
            }
        }
    }

    Doesn't work: The lambda just helps to define the type without creating the instance and calling the getter. It takes about 77ms for half million calls.

    public abstract class ModelBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            private static readonly Dictionary<Type, PropertyChangedEventArgs> Names = 
              new Dictionary<Type, PropertyChangedEventArgs>();
    
            protected void RaisePropertyChange<T>(Func<T> f)
            {
                if (PropertyChanged!=null)
                {
                    var key = typeof(T);
                    PropertyChangedEventArgs evntArgs;
                    if (!Names.TryGetValue(key, out evntArgs))
                    {
                        var propertyName = key.GetProperties()[0].Name;
                        evntArgs = new PropertyChangedEventArgs(propertyName);
                        lock(Names)
                        {
                            Names[key] = evntArgs;
                        }
                    }
                    PropertyChanged(this, evntArgs);
                }
            }
        }
    
        public class Model : ModelBase
        {
            private string firstName;
            public string FirstName
            {
                get { return firstName; }
                set {
                    if (firstName != value)
                    {
                        firstName = value;
                        RaisePropertyChange(() => new {FirstName});
                    }
                }
            }
        }

    Sunday, February 20, 2011

    jni4net 0.8.3 - release

    Changes

    • added support for IBM JRE
    • added drools sample
    • added BridgeSetup.IgnoreJavaHome
    • improved error logging during initialization

    Blocked DLLs for CLR 4.0

    If you download the distribution package on jni4net and unzip it via Windows Explorer, it will set the DLL security zone as untrusted. That will prevent CLR 4.0 from loading the DLL and you will receive exception while initializing jni4net from Java side.
    The message states:
    Can't init BridgeExport:An attempt was made to load an assembly from a network l
    ocation which would have caused the assembly to be sandboxed in previous version
    s of the .NET Framework. This release of the .NET Framework does not enable CAS
    policy by default, so this load may be dangerous. If this load is not intended t
    o sandbox the assembly, please enable the loadFromRemoteSources switch. See http
    ://go.microsoft.com/fwlink/?LinkId=155569 for more information.

    You can solve it by unblocking the DLLs.
    Right-click on the files, open file properties and click unblock button.

    Sunday, January 2, 2011

    jni4net 0.8.2 - bugfix release

    Bug fixes

    • upgraded nMaven, NUnit, made build 32bit only
    • fixed throwing exceptions for missing proxy classes
    • [#18] - C# string[]{null} -> JVM -> null reference exception
    • improved architecture detection

    Sunday, November 7, 2010

    jni4net 0.8.1 - bugfix release

    • Fixed memory leak reported by Jaco Ackermann
    • Included improved CLR detection contributed by Leonid Bogdanov
    Download 0.8.1 here

    Thursday, September 23, 2010

    Decomposition in functional languages

    Got an idea: functions (in FP) could be composed similar was as components in OOP. For example with dependency injection. For components in OOP we use interfaces. For functions we could use delegates. To achieve decomposition and modularity.
    I tried to find something about it. No luck. Probably because there are no functional programs big enough to need this ?
    While searching for enterprise scale applications written in FP I found this list. Functional Programming in the Real World and this paper

    I hope I just missed the killer application for FP, which would be big enough to deserve decomposition. Maybe someone could find me answer at stack overflow.

    Tuesday, July 20, 2010

    CLR 4.0 for Robocode

    I finally got motivated by Justin and Jason to do something for Robocode .NET again. I upgraded it to jni4net 0.8. This means Robocode will prefer CLR 4.0 if installed and then will run robots written in C# 4.0 or F#. I also implemented Robocode Control API for .NET. I piggyback to Flemming's working branch, hope he will not kill me once he returns from holidays :-D

    Download preview is there, Alpha quality.
    robocode-1.7.2.2-Alpha-setup.jar
    robocode.dotnet-1.7.2.2-Alpha-setup.jar

    MyFirstRobot.F#

    I really like the syntax.
    namespace SampleFs
    open Robocode
    type MyFirstRobot() = 
        inherit Robot()
        override robot.Run() = 
                while true do
                    robot.TurnLeft(40.0)
                    robot.Ahead(20.0)
        override robot.OnScannedRobot(evnt : ScannedRobotEvent) = 
                robot.Fire(1.0)
        override robot.OnHitByBullet(evnt : HitByBulletEvent) = 
                robot.TurnLeft(90.0 - evnt.Bearing)
    

    Sunday, May 30, 2010

    Learning F# with Project Euler - day 2

    Problem 4

    open Microsoft.FSharp.Core.Operators
    let findpalindrome xs ys =
        let separate s p = (s % (pown 10 (p+1))) - (s % (pown 10 p))
        let shiftl s p o = (separate s p) / (pown 10 (p-o))
        let shiftr s p o = (separate s p) * (pown 10 (o-p))
        let flip f = shiftl f 5 0 + shiftl f 4 1 + shiftl f 3 2 + shiftr f 2 3 + shiftr f 1 4 + shiftr f 0 5
        let findi xs y = xs |> Seq.filter( fun x -> (((x*y) = flip (x*y)) && (separate (x*y) 0 <> 0) ) ) |> Seq.map(fun x -> (x*y,x))
        ys |> Seq.collect(fun y -> (findi xs y)) |> Seq.maxBy(fun t -> fst(t))
    
    findpalindrome [100..999][100..999]
    

    Problem 5

    let test = 
        let max = (primes 20 |> Seq.reduce(fun a c -> a*c))*8L*3L
        [1L..20L] |> Seq.filter(fun x -> (max % x) <> 0L)
    

    Problem 6

    let diff xs = 
        let sumsq = xs |> Seq.reduce(fun c a -> (a*a)+c)
        let sum = (xs |> Seq.reduce(fun c a -> (a+c)))
        let sqsum  = sum*sum
        sqsum  - sumsq
    
    diff [1..100]
    

    Problem 7

    open Microsoft.FSharp.Core.Operators
    let primesnth nthprime = 
        let maxPrime = 300000
        let pr : bool array = Array.zeroCreate maxPrime
        let mutable res = 0
        let mutable nth = 0
        let mutable cur=2
        while (nth < nthprime) do
            if not pr.[cur] then
                for wr in cur+cur .. cur .. maxPrime-1 do pr.[wr] <- true
                res <- cur
                nth <- nth + 1 
            cur <- cur + 1
        res
    
    primesnth 10001
    

    Friday, May 28, 2010

    Learning F# with projecteuler.net

    Wow, lot of fun with projecteuler.net !

    Problem 1

    [1..999] |> Seq.filter( fun x -> (x % 3 = 0 || x % 5 = 0)) |> Seq.sum
    

    Problem 2

    let fib max = 
        let rec fibo a b max = 
            if b>= max then a :: [] else a :: fibo b (a+b) max
        fibo 1 2 max
    
    fib 4000000 |> Seq.filter(fun x -> (x % 2 = 0)) |> Seq.toList|> Seq.sum
    

    Problem 3

    open Microsoft.FSharp.Math 
    open System.Collections.Generic
    let maxPrime:int= (int) (System.Math.Sqrt((float)600851475143I))
    let primes maxPrime = 
        let pr : bool array = Array.zeroCreate maxPrime
        let res = new List()
        for cur in 2 .. maxPrime/2 do
            if not pr.[cur] then for wr in cur+cur .. cur .. maxPrime-1 do pr.[wr] <- true
        for cur in 1 .. maxPrime-1 do
            if not pr.[cur] then res.Add(new bigint(cur))
        res
    
    primes maxPrime |> 
        Seq.filter(fun x -> ((600851475143I % x) = 0I )) 
        |> Seq.map( fun x -> ((int)(x))) 
        |> Seq.max
    

    Saturday, April 17, 2010

    jni4net NOT yet on Mono & Linux

    I started with investigations of support for Mono on x86. The key problem is different calling convention - cdecl. My current implementation of JniLocalHandle is build on top of assumption that small structures are put onto stack same way as scalar types. But that's not valid assumption, it works just for stdcall. cdecl allocates the structure on heap and passes just pointer to the structure, no matter how big the structure is. Why I created JniLocalHandle ? Because I wanted to make strongly typed difference between JniLocalHandle and JniGlobalHandle. We could drop it and use IntPtr in order to deal with this problem. It will impact all generated proxies on C# side.
    There is another problem with cdecl, because I need to put
    [UnmanagedFunctionPointer(CallingConvention.xxx)]
    on any JNI delegate. Example is JNIEnv.AllocObject. I think I'll need to duplicate whole JNIEnv class in order to avoid condition for each call.
    Last small problem is with JNI.Dll which has the main
    [DllImport("jvm.dll", CallingConvention = CallingConvention.StdCall)]
    , it must be duplicated as well, because there is jvm.so on Linux.

    Currently I don't hear from people that they need Mono/Linux support for jni4net. If you think you need it, please tell us the use case. I'm interested to hear why Mono support is worth of the effort. Till then I put it on ice.

    jni4net version 0.8 for .NET 4.0

    • added support for CLR v 4.0
    • v40 is now loaded by default if it could be found. You could set the version explicitly with Bridge.setClrVersion()
    • #8 added BridgeSetup.AddJVMOption(string)
    Download jni4net 0.8

    Wednesday, March 3, 2010

    jni4net 0.7.1 - small bugfix release

    Bug fixes

    • [#7] - added ParPrimC2J(IntPtr)
    • [#5] - Reading Java home location from the Windows registry (Contributed by Martin Matula), BridgeSetup extended with JavaHome, and improved JavaHome auto detection
    • fixed missing assembly version for jni4net.n.*.dll