Updated to 1.10.0!
Check changes.md for what was done
This commit is contained in:
parent
67499cc5ba
commit
a07fe49880
935
README.html
935
README.html
File diff suppressed because one or more lines are too long
683
README.md
683
README.md
@ -1,6 +1,6 @@
|
||||
# multi Version: 1.9.2 (Yes I am alive, and some tweaks and additions have been made)
|
||||
# multi Version: 1.10.0 (Changelog has its own dedicated file now, also bug fixes and a new object)
|
||||
|
||||
**NOTE: I have been studying a lot about threading in the past few weeks and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua**
|
||||
**NOTE: I have been studying a lot about threading for the past few months and have some awesome additions in store! They will take a while to come out though. The goal of the library is still to provide a simple and efficient way to multi task in lua**
|
||||
|
||||
In Changes you'll find documentation for(In Order):
|
||||
- Sterilizing Objects
|
||||
@ -18,16 +18,20 @@ My multitasking library for lua. It is a pure lua binding if you ingore the inte
|
||||
|
||||
INSTALLING
|
||||
----------
|
||||
Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.2+. I will update the dependencies for luarocks since this library should work fine on lua 5.2+
|
||||
I still need to test though
|
||||
To install copy the multi folder into your enviroment and you are good to go</br>
|
||||
Note: The latest version of lualanes is required if you want to make use of system threads on lua 5.1+. I will update the dependencies for luarocks since this library should work fine on lua 5.1+
|
||||
|
||||
To install copy the multi folder into your enviroment and you are good to go</br>
|
||||
If you want to use the system threads then you'll need to install lanes!
|
||||
**or** use luarocks
|
||||
|
||||
```
|
||||
luarocks install bin -- Inorder to use the new save state stuff
|
||||
luarocks install multi
|
||||
```
|
||||
Note: In the near future you may be able to run multitasking code on multiple machines, network paralisim. This however will have to wait until I hammer out some bugs within the core of system threading itself.
|
||||
|
||||
See the rambling section to get an idea of how this will work.
|
||||
|
||||
Discord
|
||||
-------
|
||||
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries. Also you can request features and stuff there as well.</br>
|
||||
@ -58,15 +62,15 @@ Planned features/TODO
|
||||
|
||||
Known Bugs/Issues
|
||||
-----------------
|
||||
In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P
|
||||
~~In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P~~ THREAD.kill() should do the trick from within the thread. A listener could be made to detect when thread kill has been requested and sent to the running thread.
|
||||
|
||||
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now... Creating more may not be a good idea.
|
||||
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now. Going to change in a future update
|
||||
|
||||
And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.
|
||||
~~And systemThreadedTables only supports 1 table between the main and worker thread! They do not work when shared between 2 or more threads. If you need that much flexiblity ust the GLOBAL table that all threads have.~~ **FIXED**
|
||||
|
||||
For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to **n** threads and can be messed with and abused as much as you want :D
|
||||
~~For module creators using this library. I suggest using SystemThreadedQueues for data transfer instead of SystemThreadedTables for rapid data transfer, If you plan on having Constants that will always be the same then a table is a good idea! They support up to **n** threads and can be messed with and abused as much as you want :D~~ FIXED Use what you want!
|
||||
|
||||
Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!
|
||||
~~Love2D SystemThreadedTAbles do not send love2d userdata, use queues instead for that!~~ **FIXED**
|
||||
|
||||
Usage:</br>
|
||||
-----
|
||||
@ -811,619 +815,56 @@ Looping...</br>
|
||||
Looping...</br>
|
||||
We did it! 1 2 3</br>
|
||||
|
||||
Changes
|
||||
-------
|
||||
Update: 1.9.2
|
||||
-------------
|
||||
Added:
|
||||
- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
|
||||
- newTimeStamper() Part of the persistant systems... Useful for when you are running this library for a massive amount of time... like years stright!
|
||||
Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
|
||||
works for seconds, minutes, days, months, year.
|
||||
```lua
|
||||
stamper = multi:newTimeStamper()
|
||||
stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
|
||||
stamper:OnHour(int hour,func)
|
||||
stamper:OnMinute(int minute,func)
|
||||
stamper:OnSecond(int second,func)
|
||||
stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
|
||||
stamper:OnMonth(int month,func)
|
||||
stamper:OnYear(int year,func)
|
||||
```
|
||||
Updated:
|
||||
- LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment... loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results... these results however will most likely be higher than normal... different pirorities will have different default thresholds of performence.
|
||||
|
||||
Fixed:
|
||||
- Thread.getName() should now work on lanes and love2d, haven't tested ut nuch with the luvit side of things...
|
||||
- A bug with the lovemanager table.remove arguments were backwards haha
|
||||
- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
|
||||
|
||||
Changed:
|
||||
- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
|
||||
- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
|
||||
|
||||
|
||||
Update: 1.9.1
|
||||
-------------
|
||||
Added:
|
||||
- Integration "multi.integration.luvitManager"
|
||||
- Limited... Only the basic multi:newSystemThread(...) will work
|
||||
- Not even data passing will work other than arguments... If using the bin library you can pass tables and function... Even full objects as long as inner recursion is not preasent.
|
||||
|
||||
Updated:
|
||||
- multi:newSystemThread(name,func,...)
|
||||
- It will not pass the ... to the func(). Do not know why this wasn't done in the first place :P
|
||||
- Also multi:getPlatform(will now return "luvit" if using luvit... Though Idk if module creators would use the multi library when inside the luvit enviroment
|
||||
|
||||
Update: 1.9.0
|
||||
-------------
|
||||
Added:
|
||||
- multiobj:ToString() -- returns a string repersenting the object
|
||||
- multi:newFromString(str) -- creates an object from a string
|
||||
|
||||
Works on threads and regular objects. Requires the latest bin library to work!
|
||||
```lua
|
||||
talarm=multi:newThreadedAlarm("AlarmTest",5)
|
||||
talarm:OnRing(function()
|
||||
print("Ring!")
|
||||
end)
|
||||
bin.new(talarm:ToString()):tofile("test.dat")
|
||||
-- multi:newFromString(bin.load("test.dat"))
|
||||
```
|
||||
-- A more seamless way to use this will be made in the form of state saving.
|
||||
This is still a WIP
|
||||
processes, timers, timemasters, watchers, and queuers have not been worked on yet
|
||||
Update: 1.8.7
|
||||
-------------
|
||||
Added:
|
||||
- multi.timer(func,...)
|
||||
|
||||
```lua
|
||||
function test(a,b,c)
|
||||
print("Running...")
|
||||
a=0
|
||||
for i=1,1000000000 do
|
||||
a=a+1
|
||||
end
|
||||
return a,b+c
|
||||
end
|
||||
print(multi.timer(test,1,2,3))
|
||||
print(multi.timer(test,1,2,3))
|
||||
-- multi.time returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
|
||||
```
|
||||
Update: 1.8.6
|
||||
-------------
|
||||
Added:
|
||||
- jobQueue:doToAll(function)
|
||||
- jobQueue:start() is now required Call this after all calls to registerJob()'s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
|
||||
- Fixed a bug with love2d Threaded Queue
|
||||
- Fixed some bugs
|
||||
- Old versions of this library! It stems back from 2012 see rambling for more info...
|
||||
|
||||
This will run said function in every thread.
|
||||
```lua
|
||||
-- Going to use love2d code this time, almost the same as last time... See ramblings
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
require("core.GuiManager")
|
||||
gui.ff.Color=Color.Black
|
||||
jQueue=multi:newSystemThreadedJobQueue()
|
||||
jQueue:registerJob("TEST_JOB",function(a,s)
|
||||
math.randomseed(s)
|
||||
TEST_JOB2()
|
||||
return math.random(0,255)
|
||||
end)
|
||||
jQueue:registerJob("TEST_JOB2",function()
|
||||
print("Test Works!")
|
||||
end)
|
||||
-- 1.8.6 EXAMPLE Change
|
||||
jQueue:start() -- This is now needed!
|
||||
--
|
||||
jQueue:doToAll(function()
|
||||
print("Doing this 2? times!")
|
||||
end)
|
||||
tableOfOrder={}
|
||||
jQueue.OnJobCompleted(function(JOBID,n)
|
||||
tableOfOrder[JOBID]=n
|
||||
if #tableOfOrder==10 then
|
||||
t.text="We got all of the pieces!"
|
||||
end
|
||||
end)
|
||||
for i=1,10 do -- Job Name of registered function, ... varargs
|
||||
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
||||
end
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
Update: 1.8.5
|
||||
-------------
|
||||
Added:
|
||||
- SystemThreadedExecute(cmd)
|
||||
|
||||
Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
|
||||
```lua
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
|
||||
cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
|
||||
print("Got Code: "..code)
|
||||
end)
|
||||
multi:newTLoop(function()
|
||||
print("...") -- lets show that we aren't being held up
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
```
|
||||
Update: 1.8.4
|
||||
-------------
|
||||
Added:
|
||||
- multi:newSystemThreadedJobQueue()
|
||||
- Improved stability of the library
|
||||
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
|
||||
- Tweaked the loveManager to help improve idle cpu usage
|
||||
- Minor tweaks to the coroutine scheduling
|
||||
|
||||
# Using multi:newSystemThreadedJobQueue()
|
||||
First you need to create the object
|
||||
This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
|
||||
```lua
|
||||
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
|
||||
-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
|
||||
jQueue:registerJob("TEST_JOB",function(a,s)
|
||||
math.randomseed(s)
|
||||
-- We will push a random #
|
||||
TEST_JOB2() -- You can call other registered functions as well!
|
||||
return math.random(0,255) -- send the result to the main thread
|
||||
end)
|
||||
jQueue:registerJob("TEST_JOB2",function()
|
||||
print("Test Works!") -- this is called from the job since it is registered on the same queue
|
||||
end)
|
||||
tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
|
||||
jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
|
||||
-- JOBID is the completed job, starts at 1 and counts up by 1.
|
||||
-- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
|
||||
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
|
||||
if #tableOfOrder==10 then
|
||||
print("We got all of the pieces!")
|
||||
end
|
||||
end)
|
||||
-- Lets push the jobs now
|
||||
for i=1,10 do -- Job Name of registered function, ... varargs
|
||||
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
||||
end
|
||||
print("I pushed all of the jobs :)")
|
||||
multi:mainloop() -- Start the main loop :D
|
||||
```
|
||||
|
||||
Thats it from this version!
|
||||
|
||||
Update: 1.8.3
|
||||
-------------
|
||||
Added:</br>
|
||||
**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
|
||||
- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
|
||||
- multi:protectedMainloop() -- Bench: 16699308 Steps in 3 second(s)!
|
||||
- multi:unprotectedMainloop() -- Bench: 16976627 Steps in 3 second(s)!
|
||||
- multi:prioritizedMainloop1() -- Bench: 15007133 Steps in 3 second(s)!
|
||||
- multi:prioritizedMainloop2() -- Bench: 15526248 Steps in 3 second(s)!
|
||||
|
||||
\* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
|
||||
|
||||
These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
|
||||
|
||||
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
|
||||
|
||||
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
|
||||
|
||||
Update: 1.8.2
|
||||
-------------
|
||||
Added:</br>
|
||||
- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
|
||||
- Modified the GLOBAL metatable to sync before doing its tests
|
||||
- multi._VERSION was multi.Version, felt it would be more consistant this way... I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
|
||||
- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
|
||||
|
||||
The threaded table is setup just like the threaded queue.</br>
|
||||
It provids GLOBAL like features without having to write to GLOBAL!</br>
|
||||
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.</br>
|
||||
It has a few features that makes it a bit better than plain ol GLOBAL (For now...)
|
||||
(ThreadedTable - TT for short)
|
||||
- TT:waitFor(name)
|
||||
- TT:sync()
|
||||
- TT["var"]=value
|
||||
- print(TT["var"])
|
||||
|
||||
we also have the "sync" method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing 'like' syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well</br>
|
||||
|
||||
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
|
||||
|
||||
Time for some examples:
|
||||
# Using multi:newSystemThreadedTable(name)
|
||||
```lua
|
||||
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
test=multi:newSystemThreadedTable("YO"):init()
|
||||
test["test1"]="lol"
|
||||
multi:newSystemThread("test",function()
|
||||
tab=sThread.waitFor("YO"):init()
|
||||
print(tab:has("test1"))
|
||||
sThread.sleep(3)
|
||||
tab["test2"]="Whats so funny?"
|
||||
end)
|
||||
multi:newThread("test2",function()
|
||||
print(test:waitFor("test2"))
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
```lua
|
||||
-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
require("core.GuiManager")
|
||||
gui.ff.Color=Color.Black
|
||||
test=multi:newSystemThreadedTable("YO"):init()
|
||||
test["test1"]="lol"
|
||||
multi:newSystemThread("test",function()
|
||||
tab=sThread.waitFor("YO"):init()
|
||||
print(tab["test1"])
|
||||
sThread.sleep(3)
|
||||
tab["test2"]="Whats so funny?"
|
||||
end)
|
||||
multi:newThread("test2",function()
|
||||
print(test:waitFor("test2"))
|
||||
t.text="DONE!"
|
||||
end)
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
|
||||
Update: 1.8.1
|
||||
-------------
|
||||
No real change!</br>
|
||||
Changed the structure of the library. Combined the coroutine based threads into the core!</br>
|
||||
Only compat and integrations are not part of the core and never will be by nature.</br>
|
||||
This should make the library more convient to use.</br>
|
||||
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!</br>
|
||||
Updated from 1.7.6 to 1.8.0</br> (How much thread could a thread thread if a thread could thread thread?)
|
||||
Added:</br>
|
||||
- multi:newSystemThreadedQueue()
|
||||
- multi:systemThreadedBenchmark()
|
||||
- More example files
|
||||
- multi:canSystemThread() -- true if an integration was added false otherwise (For module creation)
|
||||
- Fixed a few bugs in the loveManager
|
||||
|
||||
# Using multi:systemThreadedBenchmark()
|
||||
```lua
|
||||
package.path="?/init.lua;"..package.path
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
multi:systemThreadedBenchmark(3):OnBench(function(self,count)
|
||||
print("First Bench: "..count)
|
||||
multi:systemThreadedBenchmark(3,"All Threads: ")
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Using multi:newSystemThreadedQueue()
|
||||
Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! ~~Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!</br>~~ Turns out i was wrong about this...
|
||||
```lua
|
||||
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
--IMPORTANT
|
||||
-- Do not make the above local, this is the one difference that the lanesManager does not have
|
||||
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
|
||||
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
|
||||
require("core.GuiManager")
|
||||
gui.ff.Color=Color.Black
|
||||
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||
local c={}
|
||||
c.name=name
|
||||
if love then
|
||||
if love.thread then
|
||||
function c:init()
|
||||
self.chan=love.thread.getChannel(self.name)
|
||||
function self:push(v)
|
||||
self.chan:push(v)
|
||||
end
|
||||
function self:pop()
|
||||
return self.chan:pop()
|
||||
end
|
||||
GLOBAL[self.name]=self
|
||||
return self
|
||||
end
|
||||
return c
|
||||
else
|
||||
error("Make sure you required the love.thread module!")
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda()
|
||||
function c:push(v)
|
||||
self.linda:send("Q",v)
|
||||
end
|
||||
function c:pop()
|
||||
return ({self.linda:receive(0,"Q")})[2]
|
||||
end
|
||||
function c:init()
|
||||
return self
|
||||
end
|
||||
GLOBAL[name]=c
|
||||
end
|
||||
return c
|
||||
end
|
||||
queue=multi:newSystemThreadedQueue("QUEUE"):init()
|
||||
queue:push("This is a test")
|
||||
queue:push("This is a test2")
|
||||
queue:push("This is a test3")
|
||||
queue:push("This is a test4")
|
||||
multi:newSystemThread("test2",function()
|
||||
queue=sThread.waitFor("QUEUE"):init()
|
||||
data=queue:pop()
|
||||
while data do
|
||||
print(data)
|
||||
data=queue:pop()
|
||||
end
|
||||
queue:push("DONE!")
|
||||
end)
|
||||
multi:newThread("test!",function()
|
||||
thread.hold(function() return queue:pop() end)
|
||||
t.text="Done!"
|
||||
end)
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
# In Lanes
|
||||
```lua
|
||||
-- The code is compatible with each other, I just wanted to show different things you can do in both examples
|
||||
-- This file can be found in the examples folder as lanesintegrationtest4.lua
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
queue=multi:newSystemThreadedQueue("QUEUE"):init()
|
||||
queue:push("This is a test")
|
||||
queue:push("This is a test2")
|
||||
queue:push("This is a test3")
|
||||
queue:push("This is a test4")
|
||||
multi:newSystemThread("test2",function()
|
||||
queue=sThread.waitFor("QUEUE"):init()
|
||||
data=queue:pop()
|
||||
while data do
|
||||
print(data)
|
||||
data=queue:pop()
|
||||
end
|
||||
queue:push("This is a test5")
|
||||
queue:push("This is a test6")
|
||||
queue:push("This is a test7")
|
||||
queue:push("This is a test8")
|
||||
end)
|
||||
multi:newThread("test!",function() -- this is a lua thread
|
||||
thread.sleep(.1)
|
||||
data=queue:pop()
|
||||
while data do
|
||||
print(data)
|
||||
data=queue:pop()
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
Update: 1.7.6
|
||||
-------------
|
||||
Fixed:
|
||||
Typos like always
|
||||
Added:</br>
|
||||
multi:getPlatform() -- returns "love2d" if using the love2d platform or returns "lanes" if using lanes for threading</br>
|
||||
examples files</br>
|
||||
In Events added method setTask(func)</br>
|
||||
The old way still works and is more convient to be honest, but I felt a method to do this was ok.</br>
|
||||
|
||||
Updated:
|
||||
some example files to reflect changes to the core. Changes allow for less typing</br>
|
||||
loveManager to require the compat if used so you don't need 2 require line to retrieve the library</br>
|
||||
|
||||
Update: 1.7.5
|
||||
-------------
|
||||
Fixed some typos in the readme... (I am sure there are more there are always more)</br>
|
||||
Added more features for module support</br>
|
||||
TODO:</br>
|
||||
Work on performance of the library... I see 3 places where I can make this thing run quicker</br>
|
||||
|
||||
I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!</br>
|
||||
|
||||
Update: 1.7.4
|
||||
-------------
|
||||
Added: the example folder which will be populated with more examples in the near future!</br>
|
||||
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!</br>
|
||||
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua</br>
|
||||
NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
|
||||
```lua
|
||||
require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
|
||||
require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
|
||||
--IMPORTANT
|
||||
-- Do not make the above local, this is the one difference that the lanesManager does not have
|
||||
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
|
||||
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
|
||||
require("core.GuiManager") -- allows the use of graphics in the program.
|
||||
gui.ff.Color=Color.Black
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
|
||||
GLOBAL["DONE"]=b
|
||||
end)
|
||||
multi:newThread("test0",function()
|
||||
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
|
||||
-- os.exit()
|
||||
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
|
||||
-- instead we can do this
|
||||
while true do
|
||||
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
|
||||
-- Before we held just because we could... But this is a game and we need to have logic continue
|
||||
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
|
||||
if GLOBAL["DONE"] then
|
||||
t.text="Bench: "..GLOBAL["DONE"]
|
||||
end
|
||||
end
|
||||
end)
|
||||
GLOBAL["Bench"]=3
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
Update: 1.7.3
|
||||
-------------
|
||||
Changed how requiring the library works!
|
||||
`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
|
||||
```lua
|
||||
-- This means that these are no longer required and will cause an error if done so
|
||||
require("multi.loop")
|
||||
require("multi.alarm")
|
||||
require("multi.updater")
|
||||
require("multi.tloop")
|
||||
require("multi.watcher")
|
||||
require("multi.tstep")
|
||||
require("multi.step")
|
||||
require("multi.task")
|
||||
-- ^ they are all part of the core now
|
||||
```
|
||||
|
||||
Update: 1.7.2
|
||||
-------------
|
||||
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
|
||||
|
||||
Update: 1.7.1 Bug Fixes Only
|
||||
-------------
|
||||
|
||||
Update: 1.7.0
|
||||
-------------
|
||||
Modified: multi.integration.lanesManager.lua
|
||||
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.</br>
|
||||
Example Usage:</br>
|
||||
sThread is a handle to a global interface for system threads to interact with themself</br>
|
||||
thread is the interface for multithreads as seen in the threading section</br>
|
||||
|
||||
GLOBAL a table that can be used throughout each and every thread
|
||||
|
||||
sThreads have a few methods</br>
|
||||
sThread.set(name,val) -- you can use the GLOBAL table instead modifies the same table anyway</br>
|
||||
sThread.get(name) -- you can use the GLOBAL table instead modifies the same table anyway</br>
|
||||
sThread.waitFor(name) -- waits until a value exists, if it does it returns it</br>
|
||||
sThread.getCores() -- returns the number of cores on your cpu</br>
|
||||
sThread.sleep(n) -- sleeps for a bit stopping the entire thread from running</br>
|
||||
sThread.hold(n) -- sleeps until a condition is met</br>
|
||||
```lua
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
require("multi.all")
|
||||
multi:newAlarm(2):OnRing(function(self)
|
||||
GLOBAL["NumOfCores"]=sThread.getCores()
|
||||
end)
|
||||
multi:newAlarm(7):OnRing(function(self)
|
||||
GLOBAL["AnotherTest"]=true
|
||||
end)
|
||||
multi:newAlarm(13):OnRing(function(self)
|
||||
GLOBAL["FinalTest"]=true
|
||||
end)
|
||||
multi:newSystemThread("test",function() -- spawns a thread in another lua process
|
||||
require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
|
||||
print("Waiting for variable: NumOfCores")
|
||||
print("Got it: ",sThread.waitFor("NumOfCores"))
|
||||
sThread.hold(function()
|
||||
return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
|
||||
end)
|
||||
print("Holding works!")
|
||||
multi:newThread("tests",function()
|
||||
thread.hold(function()
|
||||
return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
|
||||
end)
|
||||
print("Final test works!")
|
||||
os.exit()
|
||||
end)
|
||||
local a=0
|
||||
multi:newTLoop(function()
|
||||
a=a+1
|
||||
print(a)
|
||||
end,.5)
|
||||
multi:mainloop()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
Update: 1.6.0
|
||||
-------------
|
||||
Changed: steps and loops
|
||||
```lua
|
||||
-- Was
|
||||
step:OnStep(function(pos,self) -- same goes for tsteps as well
|
||||
print(pos)
|
||||
end)
|
||||
multi:newLoop(function(dt,self)
|
||||
print(dt)
|
||||
end)
|
||||
-- Is now
|
||||
step:OnStep(function(self,pos) -- same goes for tsteps as well
|
||||
print(pos)
|
||||
end)
|
||||
multi:newLoop(function(self,dt)
|
||||
print(dt)
|
||||
end)
|
||||
```
|
||||
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
|
||||
```lua
|
||||
require("multi.all")
|
||||
require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
|
||||
```
|
||||
Update: 1.5.0
|
||||
-------------
|
||||
Added:
|
||||
- An easy way to manage timeouts
|
||||
- Small bug fixes
|
||||
|
||||
Update: 1.4.1 - First Public release of the library
|
||||
-------------
|
||||
|
||||
IMPORTANT:</br>
|
||||
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.</br>
|
||||
Rambling
|
||||
--------
|
||||
5/23/18:
|
||||
When it comes to running code across different systems we run into a problem. It takes time to send objects from one maching to another. In the beginning only local networks will be supported. I may add support to send commands to another network to do computing. Like having your own lus cloud. userdata will never be allowed to run on other machines. It is not possible unless the library you are using allows userdata to be turned into a string and back into an object. With this feature you want to send a command that will take time or needs tons of them done millions+, reason being networks are not that "fast" and only simple objects can be sent. If you mirror your enviroment then you can do some cool things.
|
||||
|
||||
The planned structure will be something like this:
|
||||
multi-Single Threaded Multitasking
|
||||
multi-Threads
|
||||
multi-System Threads
|
||||
multi-Network threads
|
||||
|
||||
where netThreads can contain systemThreads which can intern contain both Threads and single threaded multitasking
|
||||
|
||||
Nothing has been built yet, but the system will work something like this:
|
||||
#host:
|
||||
```lua
|
||||
sGLOBAL, nGlobal,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
|
||||
multi:Host("MainSystem") -- tell the network that this is the main system. Uses broadcast so that nodes know how to find the host!
|
||||
nThread = multi:newNetworkThread("NetThread_1",function(...)
|
||||
-- basic usage
|
||||
nGLOBAL["RemoteVaraible"] = true -- will sync data to all nodes and the host
|
||||
sGLOBAL["LocalMachineVaraible"] = true -- will sync data to all system threads on the local machine
|
||||
return "Hello Network!" -- send "Hello Network" back to the host node
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
#node
|
||||
```lua
|
||||
GLOBAL,sThread=require("multi.integration.networkManager").init() -- This will determine if one is using lanes,love2d, or luvit
|
||||
node = multi:newNode("NodeName","MainSystem") -- Search the network for the host, connect to it and be ready for requests!
|
||||
-- On the main thread, a simple multi:newNetworkThread thread and also non system threads, you can access global data without an issue. When dealing with system threads is when you have a problem.
|
||||
node:setLog{
|
||||
maxLines = 10000,
|
||||
cleanOnInterval = true,
|
||||
cleanInterval = "day", -- every day Supports(day, week, month, year)
|
||||
noLog = false -- default is false, make true if you do not need a log
|
||||
}
|
||||
node:settings{
|
||||
maxJobs = 100, -- Job queues will respect this as well as the host when it is figuting out which node is under the least load. Default: 0 or infinite
|
||||
sendLoadInterval = 60 -- every 60 seconds update the host of the nodes load
|
||||
sendLoad = true -- default is true, tells the server how stressed the system is
|
||||
}
|
||||
multi:mainloop()
|
||||
-- Note: the node will contain a log of all the commands that it gets. A file called "NodeName.log" will contain the info. You can set the limit by lines or file size. Also you can set it to clear the log every interval of time if an error does not exist. All errors are both logged and sent to the host as well. You can have more than one host and more than one node(duh :P).
|
||||
```
|
||||
The goal of the node is to set up a simple and easy way to run commands on a remote machine.
|
||||
|
||||
There are 2 main ways you can use this feature. 1. One node per machine with system threads being able to use the full processing power of the machine. 2. Multiple nodes on one machine where each node is acting like its own thread. And of course a mix of the two is indeed possible.
|
||||
|
||||
|
||||
Love2d Sleeping reduces the cpu time making my load detection think the system is under more load, thus preventing it from sleeping... I will look into other means. As of right now it will not eat all of your cpu if threads are active. For now I suggest killing threads that aren't needed anymore. On lanes threads at idle use 0% cpu and it is amazing. A state machine may solve what I need though. One state being idle state that sleeps and only goes into the active state if a job request or data is sent to it... after some time of not being under load it wil switch back into the idle state... We'll see what happens.
|
||||
|
||||
Love2d doesn't like to send functions through channels. By defualt it does not support this. I achieve this by dumping the function and loadstring it on the thread. This however is slow. For the System Threaded Job Queue I had to change my original idea of sending functions as jobs. The current way you do it now is register a job functions once and then call that job across the thread through a queue. Each worker thread pops from the queue and returns the job. The Job ID is automatically updated and allows you to keep track of the order that the data comes in. A table with # indexes can be used to originze the data...
|
||||
|
||||
868
changes.html
Normal file
868
changes.html
Normal file
File diff suppressed because one or more lines are too long
693
changes.md
Normal file
693
changes.md
Normal file
@ -0,0 +1,693 @@
|
||||
Changes
|
||||
-------
|
||||
Update: 1.10.0
|
||||
-------------
|
||||
**Note:** The library is now considered to be stable!
|
||||
**Upcoming:** Network parallelism is on the way. It is in the works and should be released soon
|
||||
|
||||
Added:
|
||||
------
|
||||
- isMainThread true/nil
|
||||
- multi:newSystemThreadedConnection(name,protect) -- Works like normal connections, but are able to trigger events across threads
|
||||
|
||||
Example of threaded connections
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
local GLOBAL,THREAD=require("multi.integration.lanesManager").init()
|
||||
multi:newSystemThread("Test_Thread_1",function()
|
||||
connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
|
||||
connOut(function(arg)
|
||||
print(THREAD.getName(),arg)
|
||||
end)
|
||||
multi:mainloop()
|
||||
end)
|
||||
multi:newSystemThread("Test_Thread_2",function()
|
||||
connOut = THREAD.waitFor("ConnectionNAMEHERE"):init()
|
||||
connOut(function(arg)
|
||||
print(THREAD.getName(),arg)
|
||||
end)
|
||||
multi:mainloop()
|
||||
end)
|
||||
connOut = multi:newSystemThreadedConnection("ConnectionNAMEHERE"):init()
|
||||
a=0
|
||||
connOut(function(arg)
|
||||
print("Main",arg)
|
||||
end)
|
||||
multi:newTLoop(function()
|
||||
a=a+1
|
||||
connOut:Fire("Test From Main Thread: "..a.."\n")
|
||||
end,1)
|
||||
```
|
||||
|
||||
Fixed:
|
||||
------
|
||||
**loveManager** and **shared threading objects**
|
||||
- sThread.waitFor()
|
||||
- sThread.hold()
|
||||
- some typos
|
||||
- SystemThreadedTables (They now work on both lanes and love2d as expected)
|
||||
|
||||
Example of threaded tables
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
multi:newSystemThread("Test_Thread_1",function()
|
||||
require("multi")
|
||||
test = sThread.waitFor("testthing"):init()
|
||||
multi:newTLoop(function()
|
||||
print("------")
|
||||
for i,v in pairs(test.tab) do
|
||||
print("T1",i,v)
|
||||
end
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
end)
|
||||
multi:newSystemThread("Test_Thread_1",function()
|
||||
require("multi")
|
||||
test = sThread.waitFor("testthing"):init()
|
||||
multi:newTLoop(function()
|
||||
print("------")
|
||||
for i,v in pairs(test.tab) do
|
||||
print("T2",i,v)
|
||||
end
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
end)
|
||||
test = multi:newSystemThreadedTable("testthing"):init()
|
||||
multi:newTLoop(function()
|
||||
local a,b = multi.randomString(8),multi.randomString(4)
|
||||
print(">",a,b)
|
||||
test[a]=b
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
Update: 1.9.2
|
||||
-------------
|
||||
Added:
|
||||
- (THREAD).kill() kills a thread. Note: THREAD is based on what you name it
|
||||
- newTimeStamper() Part of the persistant systems... Useful for when you are running this library for a massive amount of time... like years stright!
|
||||
Allows one to hook to timed events such as whenever the clock strikes midnight or when the day turns to monday. The event is only done once though. so as soon as monday is set it would trigger then not trigger again until next monday
|
||||
works for seconds, minutes, days, months, year.
|
||||
```lua
|
||||
stamper = multi:newTimeStamper()
|
||||
stamper:OnTime(int hour,int minute,int second,func) or stamper:OnTime(string time,func) time as 00:00:00
|
||||
stamper:OnHour(int hour,func)
|
||||
stamper:OnMinute(int minute,func)
|
||||
stamper:OnSecond(int second,func)
|
||||
stamper:OnDay(int day,func) or stamper:OnDay(string day,func) Mon, Tues, Wed, etc...
|
||||
stamper:OnMonth(int month,func)
|
||||
stamper:OnYear(int year,func)
|
||||
```
|
||||
Updated:
|
||||
- LoadBalancing, well bettwr load balancing than existed before. This one allowd for multiple processes to have their own load reading. Calling this on the multi object will return the total load for the entire multi enviroment... loads of other processes are indeed affected by what other processes are doing. However if you combine prorioty to the mix of things then you will get differing results... these results however will most likely be higher than normal... different pirorities will have different default thresholds of performence.
|
||||
|
||||
Fixed:
|
||||
- Thread.getName() should now work on lanes and love2d, haven't tested ut nuch with the luvit side of things...
|
||||
- A bug with the lovemanager table.remove arguments were backwards haha
|
||||
- The queue object in the love2d threading has been fixed! It now supports sending all objects (even functions as long as no upvalues are present!)
|
||||
|
||||
Changed:
|
||||
- SystemThreadedJobQueues now have built in load management so they are not constantly at 100% cpu usage.
|
||||
- SystemThreadedJobQueues pushJob now retunts an id of that job which will match the same one that OnJobCompleted returns
|
||||
|
||||
|
||||
Update: 1.9.1
|
||||
-------------
|
||||
Added:
|
||||
- Integration "multi.integration.luvitManager"
|
||||
- Limited... Only the basic multi:newSystemThread(...) will work
|
||||
- Not even data passing will work other than arguments... If using the bin library you can pass tables and function... Even full objects as long as inner recursion is not preasent.
|
||||
|
||||
Updated:
|
||||
- multi:newSystemThread(name,func,...)
|
||||
- It will not pass the ... to the func(). Do not know why this wasn't done in the first place :P
|
||||
- Also multi:getPlatform(will now return "luvit" if using luvit... Though Idk if module creators would use the multi library when inside the luvit enviroment
|
||||
|
||||
Update: 1.9.0
|
||||
-------------
|
||||
Added:
|
||||
- multiobj:ToString() -- returns a string repersenting the object
|
||||
- multi:newFromString(str) -- creates an object from a string
|
||||
|
||||
Works on threads and regular objects. Requires the latest bin library to work!
|
||||
```lua
|
||||
talarm=multi:newThreadedAlarm("AlarmTest",5)
|
||||
talarm:OnRing(function()
|
||||
print("Ring!")
|
||||
end)
|
||||
bin.new(talarm:ToString()):tofile("test.dat")
|
||||
-- multi:newFromString(bin.load("test.dat"))
|
||||
```
|
||||
-- A more seamless way to use this will be made in the form of state saving.
|
||||
This is still a WIP
|
||||
processes, timers, timemasters, watchers, and queuers have not been worked on yet
|
||||
Update: 1.8.7
|
||||
-------------
|
||||
Added:
|
||||
- multi.timer(func,...)
|
||||
|
||||
```lua
|
||||
function test(a,b,c)
|
||||
print("Running...")
|
||||
a=0
|
||||
for i=1,1000000000 do
|
||||
a=a+1
|
||||
end
|
||||
return a,b+c
|
||||
end
|
||||
print(multi.timer(test,1,2,3))
|
||||
print(multi.timer(test,1,2,3))
|
||||
-- multi.timer returns the time taken then the arguments from the function... Uses unpack so careful of nil values!
|
||||
```
|
||||
Update: 1.8.6
|
||||
-------------
|
||||
Added:
|
||||
- jobQueue:doToAll(function)
|
||||
- jobQueue:start() is now required Call this after all calls to registerJob()'s. Calling it afterwards will not guarantee your next push job with that job will work. Not calling this will make pushing jobs impossible!
|
||||
- Fixed a bug with love2d Threaded Queue
|
||||
- Fixed some bugs
|
||||
- Old versions of this library! It stems back from 2012 see rambling for more info...
|
||||
|
||||
This will run said function in every thread.
|
||||
```lua
|
||||
-- Going to use love2d code this time, almost the same as last time... See ramblings
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
require("core.GuiManager")
|
||||
gui.ff.Color=Color.Black
|
||||
jQueue=multi:newSystemThreadedJobQueue()
|
||||
jQueue:registerJob("TEST_JOB",function(a,s)
|
||||
math.randomseed(s)
|
||||
TEST_JOB2()
|
||||
return math.random(0,255)
|
||||
end)
|
||||
jQueue:registerJob("TEST_JOB2",function()
|
||||
print("Test Works!")
|
||||
end)
|
||||
-- 1.8.6 EXAMPLE Change
|
||||
jQueue:start() -- This is now needed!
|
||||
--
|
||||
jQueue:doToAll(function()
|
||||
print("Doing this 2? times!")
|
||||
end)
|
||||
tableOfOrder={}
|
||||
jQueue.OnJobCompleted(function(JOBID,n)
|
||||
tableOfOrder[JOBID]=n
|
||||
if #tableOfOrder==10 then
|
||||
t.text="We got all of the pieces!"
|
||||
end
|
||||
end)
|
||||
for i=1,10 do -- Job Name of registered function, ... varargs
|
||||
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
||||
end
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
Update: 1.8.5
|
||||
-------------
|
||||
Added:
|
||||
- SystemThreadedExecute(cmd)
|
||||
|
||||
Allows the execution of system calls without hold up. It is possible to do the same using io.popen()! You decide which works best for you!
|
||||
```lua
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
cmd=multi:newSystemThreadedExecute("SystemThreadedExecuteTest.lua") -- This file is important!
|
||||
cmd.OnCMDFinished(function(code) -- callback function to grab the exit code... Called when the command goes through
|
||||
print("Got Code: "..code)
|
||||
end)
|
||||
multi:newTLoop(function()
|
||||
print("...") -- lets show that we aren't being held up
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
```
|
||||
Update: 1.8.4
|
||||
-------------
|
||||
Added:
|
||||
- multi:newSystemThreadedJobQueue()
|
||||
- Improved stability of the library
|
||||
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
|
||||
- Tweaked the loveManager to help improve idle cpu usage
|
||||
- Minor tweaks to the coroutine scheduling
|
||||
|
||||
# Using multi:newSystemThreadedJobQueue()
|
||||
First you need to create the object
|
||||
This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
|
||||
```lua
|
||||
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
|
||||
-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
|
||||
jQueue:registerJob("TEST_JOB",function(a,s)
|
||||
math.randomseed(s)
|
||||
-- We will push a random #
|
||||
TEST_JOB2() -- You can call other registered functions as well!
|
||||
return math.random(0,255) -- send the result to the main thread
|
||||
end)
|
||||
jQueue:registerJob("TEST_JOB2",function()
|
||||
print("Test Works!") -- this is called from the job since it is registered on the same queue
|
||||
end)
|
||||
tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
|
||||
jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
|
||||
-- JOBID is the completed job, starts at 1 and counts up by 1.
|
||||
-- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
|
||||
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
|
||||
if #tableOfOrder==10 then
|
||||
print("We got all of the pieces!")
|
||||
end
|
||||
end)
|
||||
-- Lets push the jobs now
|
||||
for i=1,10 do -- Job Name of registered function, ... varargs
|
||||
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
||||
end
|
||||
print("I pushed all of the jobs :)")
|
||||
multi:mainloop() -- Start the main loop :D
|
||||
```
|
||||
|
||||
Thats it from this version!
|
||||
|
||||
Update: 1.8.3
|
||||
-------------
|
||||
Added:</br>
|
||||
**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
|
||||
- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
|
||||
- multi:protectedMainloop() -- Bench: 16699308 Steps in 3 second(s)!
|
||||
- multi:unprotectedMainloop() -- Bench: 16976627 Steps in 3 second(s)!
|
||||
- multi:prioritizedMainloop1() -- Bench: 15007133 Steps in 3 second(s)!
|
||||
- multi:prioritizedMainloop2() -- Bench: 15526248 Steps in 3 second(s)!
|
||||
|
||||
\* The OG mainloop function remains the same and old methods to achieve what we have with the new ones still exist
|
||||
|
||||
These new methods help by removing function overhead that is caused through the original mainloop function. The one downside is that you no longer have the flexiblity to change the processing during runtime.
|
||||
|
||||
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
|
||||
|
||||
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
|
||||
|
||||
Update: 1.8.2
|
||||
-------------
|
||||
Added:</br>
|
||||
- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
|
||||
- Modified the GLOBAL metatable to sync before doing its tests
|
||||
- multi._VERSION was multi.Version, felt it would be more consistant this way... I left the old way of getting the version just incase someone has used that way. It will eventually be gone. Also multi:getVersion() will do the job just as well and keep your code nice and update related bug free!
|
||||
- Also everything that is included in the: multi/integration/shared/shared.lua (Which is loaded automatically) works in both lanes and love2d enviroments!
|
||||
|
||||
The threaded table is setup just like the threaded queue.</br>
|
||||
It provids GLOBAL like features without having to write to GLOBAL!</br>
|
||||
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.</br>
|
||||
It has a few features that makes it a bit better than plain ol GLOBAL (For now...)
|
||||
(ThreadedTable - TT for short)
|
||||
- TT:waitFor(name)
|
||||
- TT:sync()
|
||||
- TT["var"]=value
|
||||
- print(TT["var"])
|
||||
|
||||
we also have the "sync" method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing 'like' syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well</br>
|
||||
|
||||
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
|
||||
|
||||
Time for some examples:
|
||||
# Using multi:newSystemThreadedTable(name)
|
||||
```lua
|
||||
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
test=multi:newSystemThreadedTable("YO"):init()
|
||||
test["test1"]="lol"
|
||||
multi:newSystemThread("test",function()
|
||||
tab=sThread.waitFor("YO"):init()
|
||||
print(tab:has("test1"))
|
||||
sThread.sleep(3)
|
||||
tab["test2"]="Whats so funny?"
|
||||
end)
|
||||
multi:newThread("test2",function()
|
||||
print(test:waitFor("test2"))
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
```lua
|
||||
-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
require("core.GuiManager")
|
||||
gui.ff.Color=Color.Black
|
||||
test=multi:newSystemThreadedTable("YO"):init()
|
||||
test["test1"]="lol"
|
||||
multi:newSystemThread("test",function()
|
||||
tab=sThread.waitFor("YO"):init()
|
||||
print(tab["test1"])
|
||||
sThread.sleep(3)
|
||||
tab["test2"]="Whats so funny?"
|
||||
end)
|
||||
multi:newThread("test2",function()
|
||||
print(test:waitFor("test2"))
|
||||
t.text="DONE!"
|
||||
end)
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
|
||||
Update: 1.8.1
|
||||
-------------
|
||||
No real change!</br>
|
||||
Changed the structure of the library. Combined the coroutine based threads into the core!</br>
|
||||
Only compat and integrations are not part of the core and never will be by nature.</br>
|
||||
This should make the library more convient to use.</br>
|
||||
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!</br>
|
||||
Updated from 1.7.6 to 1.8.0</br> (How much thread could a thread thread if a thread could thread thread?)
|
||||
Added:</br>
|
||||
- multi:newSystemThreadedQueue()
|
||||
- multi:systemThreadedBenchmark()
|
||||
- More example files
|
||||
- multi:canSystemThread() -- true if an integration was added false otherwise (For module creation)
|
||||
- Fixed a few bugs in the loveManager
|
||||
|
||||
# Using multi:systemThreadedBenchmark()
|
||||
```lua
|
||||
package.path="?/init.lua;"..package.path
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
multi:systemThreadedBenchmark(3):OnBench(function(self,count)
|
||||
print("First Bench: "..count)
|
||||
multi:systemThreadedBenchmark(3,"All Threads: ")
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Using multi:newSystemThreadedQueue()
|
||||
Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! ~~Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!</br>~~ Turns out i was wrong about this...
|
||||
```lua
|
||||
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
--IMPORTANT
|
||||
-- Do not make the above local, this is the one difference that the lanesManager does not have
|
||||
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
|
||||
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
|
||||
require("core.GuiManager")
|
||||
gui.ff.Color=Color.Black
|
||||
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||
local c={}
|
||||
c.name=name
|
||||
if love then
|
||||
if love.thread then
|
||||
function c:init()
|
||||
self.chan=love.thread.getChannel(self.name)
|
||||
function self:push(v)
|
||||
self.chan:push(v)
|
||||
end
|
||||
function self:pop()
|
||||
return self.chan:pop()
|
||||
end
|
||||
GLOBAL[self.name]=self
|
||||
return self
|
||||
end
|
||||
return c
|
||||
else
|
||||
error("Make sure you required the love.thread module!")
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda()
|
||||
function c:push(v)
|
||||
self.linda:send("Q",v)
|
||||
end
|
||||
function c:pop()
|
||||
return ({self.linda:receive(0,"Q")})[2]
|
||||
end
|
||||
function c:init()
|
||||
return self
|
||||
end
|
||||
GLOBAL[name]=c
|
||||
end
|
||||
return c
|
||||
end
|
||||
queue=multi:newSystemThreadedQueue("QUEUE"):init()
|
||||
queue:push("This is a test")
|
||||
queue:push("This is a test2")
|
||||
queue:push("This is a test3")
|
||||
queue:push("This is a test4")
|
||||
multi:newSystemThread("test2",function()
|
||||
queue=sThread.waitFor("QUEUE"):init()
|
||||
data=queue:pop()
|
||||
while data do
|
||||
print(data)
|
||||
data=queue:pop()
|
||||
end
|
||||
queue:push("DONE!")
|
||||
end)
|
||||
multi:newThread("test!",function()
|
||||
thread.hold(function() return queue:pop() end)
|
||||
t.text="Done!"
|
||||
end)
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
# In Lanes
|
||||
```lua
|
||||
-- The code is compatible with each other, I just wanted to show different things you can do in both examples
|
||||
-- This file can be found in the examples folder as lanesintegrationtest4.lua
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
queue=multi:newSystemThreadedQueue("QUEUE"):init()
|
||||
queue:push("This is a test")
|
||||
queue:push("This is a test2")
|
||||
queue:push("This is a test3")
|
||||
queue:push("This is a test4")
|
||||
multi:newSystemThread("test2",function()
|
||||
queue=sThread.waitFor("QUEUE"):init()
|
||||
data=queue:pop()
|
||||
while data do
|
||||
print(data)
|
||||
data=queue:pop()
|
||||
end
|
||||
queue:push("This is a test5")
|
||||
queue:push("This is a test6")
|
||||
queue:push("This is a test7")
|
||||
queue:push("This is a test8")
|
||||
end)
|
||||
multi:newThread("test!",function() -- this is a lua thread
|
||||
thread.sleep(.1)
|
||||
data=queue:pop()
|
||||
while data do
|
||||
print(data)
|
||||
data=queue:pop()
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
Update: 1.7.6
|
||||
-------------
|
||||
Fixed:
|
||||
Typos like always
|
||||
Added:</br>
|
||||
multi:getPlatform() -- returns "love2d" if using the love2d platform or returns "lanes" if using lanes for threading</br>
|
||||
examples files</br>
|
||||
In Events added method setTask(func)</br>
|
||||
The old way still works and is more convient to be honest, but I felt a method to do this was ok.</br>
|
||||
|
||||
Updated:
|
||||
some example files to reflect changes to the core. Changes allow for less typing</br>
|
||||
loveManager to require the compat if used so you don't need 2 require line to retrieve the library</br>
|
||||
|
||||
Update: 1.7.5
|
||||
-------------
|
||||
Fixed some typos in the readme... (I am sure there are more there are always more)</br>
|
||||
Added more features for module support</br>
|
||||
TODO:</br>
|
||||
Work on performance of the library... I see 3 places where I can make this thing run quicker</br>
|
||||
|
||||
I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!</br>
|
||||
|
||||
Update: 1.7.4
|
||||
-------------
|
||||
Added: the example folder which will be populated with more examples in the near future!</br>
|
||||
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!</br>
|
||||
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua</br>
|
||||
NOTE: This code has only been tested to work on love2d version 1.10.2 thoough it should work version 0.9.0
|
||||
```lua
|
||||
require("core.Library") -- Didn't add this to a repo yet! Will do eventually... Allows for injections and other cool things
|
||||
require("multi.compat.love2d") -- allows for multitasking and binds my libraies to the love2d engine that i am using
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager
|
||||
--IMPORTANT
|
||||
-- Do not make the above local, this is the one difference that the lanesManager does not have
|
||||
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
|
||||
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
|
||||
require("core.GuiManager") -- allows the use of graphics in the program.
|
||||
gui.ff.Color=Color.Black
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
|
||||
GLOBAL["DONE"]=b
|
||||
end)
|
||||
multi:newThread("test0",function()
|
||||
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
|
||||
-- os.exit()
|
||||
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
|
||||
-- instead we can do this
|
||||
while true do
|
||||
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
|
||||
-- Before we held just because we could... But this is a game and we need to have logic continue
|
||||
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
|
||||
if GLOBAL["DONE"] then
|
||||
t.text="Bench: "..GLOBAL["DONE"]
|
||||
end
|
||||
end
|
||||
end)
|
||||
GLOBAL["Bench"]=3
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
```
|
||||
Update: 1.7.3
|
||||
-------------
|
||||
Changed how requiring the library works!
|
||||
`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
|
||||
```lua
|
||||
-- This means that these are no longer required and will cause an error if done so
|
||||
require("multi.loop")
|
||||
require("multi.alarm")
|
||||
require("multi.updater")
|
||||
require("multi.tloop")
|
||||
require("multi.watcher")
|
||||
require("multi.tstep")
|
||||
require("multi.step")
|
||||
require("multi.task")
|
||||
-- ^ they are all part of the core now
|
||||
```
|
||||
|
||||
Update: 1.7.2
|
||||
-------------
|
||||
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
|
||||
|
||||
Update: 1.7.1 Bug Fixes Only
|
||||
-------------
|
||||
|
||||
Update: 1.7.0
|
||||
-------------
|
||||
Modified: multi.integration.lanesManager.lua
|
||||
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.</br>
|
||||
Example Usage:</br>
|
||||
sThread is a handle to a global interface for system threads to interact with themself</br>
|
||||
thread is the interface for multithreads as seen in the threading section</br>
|
||||
|
||||
GLOBAL a table that can be used throughout each and every thread
|
||||
|
||||
sThreads have a few methods</br>
|
||||
sThread.set(name,val) -- you can use the GLOBAL table instead modifies the same table anyway</br>
|
||||
sThread.get(name) -- you can use the GLOBAL table instead modifies the same table anyway</br>
|
||||
sThread.waitFor(name) -- waits until a value exists, if it does it returns it</br>
|
||||
sThread.getCores() -- returns the number of cores on your cpu</br>
|
||||
sThread.sleep(n) -- sleeps for a bit stopping the entire thread from running</br>
|
||||
sThread.hold(n) -- sleeps until a condition is met</br>
|
||||
```lua
|
||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||
require("multi.all")
|
||||
multi:newAlarm(2):OnRing(function(self)
|
||||
GLOBAL["NumOfCores"]=sThread.getCores()
|
||||
end)
|
||||
multi:newAlarm(7):OnRing(function(self)
|
||||
GLOBAL["AnotherTest"]=true
|
||||
end)
|
||||
multi:newAlarm(13):OnRing(function(self)
|
||||
GLOBAL["FinalTest"]=true
|
||||
end)
|
||||
multi:newSystemThread("test",function() -- spawns a thread in another lua process
|
||||
require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the integration. You would need to require the interaction again though
|
||||
print("Waiting for variable: NumOfCores")
|
||||
print("Got it: ",sThread.waitFor("NumOfCores"))
|
||||
sThread.hold(function()
|
||||
return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
|
||||
end)
|
||||
print("Holding works!")
|
||||
multi:newThread("tests",function()
|
||||
thread.hold(function()
|
||||
return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
|
||||
end)
|
||||
print("Final test works!")
|
||||
os.exit()
|
||||
end)
|
||||
local a=0
|
||||
multi:newTLoop(function()
|
||||
a=a+1
|
||||
print(a)
|
||||
end,.5)
|
||||
multi:mainloop()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
Update: 1.6.0
|
||||
-------------
|
||||
Changed: steps and loops
|
||||
```lua
|
||||
-- Was
|
||||
step:OnStep(function(pos,self) -- same goes for tsteps as well
|
||||
print(pos)
|
||||
end)
|
||||
multi:newLoop(function(dt,self)
|
||||
print(dt)
|
||||
end)
|
||||
-- Is now
|
||||
step:OnStep(function(self,pos) -- same goes for tsteps as well
|
||||
print(pos)
|
||||
end)
|
||||
multi:newLoop(function(self,dt)
|
||||
print(dt)
|
||||
end)
|
||||
```
|
||||
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
|
||||
```lua
|
||||
require("multi.all")
|
||||
require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
|
||||
```
|
||||
Update: 1.5.0
|
||||
-------------
|
||||
Added:
|
||||
- An easy way to manage timeouts
|
||||
- Small bug fixes
|
||||
|
||||
Update: 1.4.1 - First Public release of the library
|
||||
-------------
|
||||
|
||||
**IMPORTANT:**
|
||||
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.</br>
|
||||
73
examples/love2d Threading Example/bin/compressors/lzw.lua
Normal file
73
examples/love2d Threading Example/bin/compressors/lzw.lua
Normal file
@ -0,0 +1,73 @@
|
||||
--[[
|
||||
LZW String Compression demo for Gideros
|
||||
This code is MIT licensed, see http://www.opensource.org/licenses/mit-license.php
|
||||
(C) 2013 - Guava7
|
||||
]]
|
||||
CLZWCompression = {}
|
||||
function CLZWCompression:InitDictionary(isEncode)
|
||||
self.mDictionary = {}
|
||||
-- local s = " !#$%&'\"()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||
local s={}
|
||||
for i=1,255 do
|
||||
s[#s+1]=string.char(i)
|
||||
end
|
||||
s=table.concat(s)
|
||||
local len = #s
|
||||
for i = 1, len do
|
||||
if isEncode then
|
||||
self.mDictionary[s:sub(i, i)] = i
|
||||
else
|
||||
self.mDictionary[i] = s:sub(i, i)
|
||||
end
|
||||
end
|
||||
self.mDictionaryLen = len
|
||||
end
|
||||
function CLZWCompression:Encode(sInput)
|
||||
self:InitDictionary(true)
|
||||
local s = ""
|
||||
local ch
|
||||
local len = #sInput
|
||||
local result = {}
|
||||
local dic = self.mDictionary
|
||||
local temp
|
||||
for i = 1, len do
|
||||
ch = sInput:sub(i, i)
|
||||
temp = s..ch
|
||||
if dic[temp] then
|
||||
s = temp
|
||||
else
|
||||
result[#result + 1] = dic[s]
|
||||
self.mDictionaryLen = self.mDictionaryLen + 1
|
||||
dic[temp] = self.mDictionaryLen
|
||||
s = ch
|
||||
end
|
||||
end
|
||||
result[#result + 1] = dic[s]
|
||||
return result
|
||||
end
|
||||
function CLZWCompression:Decode(data)
|
||||
self:InitDictionary(false)
|
||||
local dic = self.mDictionary
|
||||
local entry
|
||||
local ch
|
||||
local prevCode, currCode
|
||||
local result = {}
|
||||
prevCode = data[1]
|
||||
result[#result + 1] = dic[prevCode]
|
||||
for i = 2, #data do
|
||||
currCode = data[i]
|
||||
entry = dic[currCode]
|
||||
if entry then
|
||||
ch = entry:sub(1, 1)
|
||||
result[#result + 1] = entry
|
||||
else
|
||||
ch = dic[prevCode]:sub(1, 1)
|
||||
result[#result + 1] = dic[prevCode]..ch
|
||||
end
|
||||
dic[#dic + 1] = dic[prevCode]..ch
|
||||
prevCode = currCode
|
||||
end
|
||||
return table.concat(result)
|
||||
end
|
||||
|
||||
return CLZWCompression
|
||||
30
examples/love2d Threading Example/bin/converters/base64.lua
Normal file
30
examples/love2d Threading Example/bin/converters/base64.lua
Normal file
@ -0,0 +1,30 @@
|
||||
local base64={}
|
||||
local bs = { [0] =
|
||||
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
|
||||
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
|
||||
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
|
||||
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
|
||||
}
|
||||
local bsd=table.flip(bs)
|
||||
local char=string.char
|
||||
function base64.encode(s)
|
||||
local byte, rep, pad = string.byte, string.rep, 2 - ((#s-1) % 3)
|
||||
s = (s..rep('\0', pad)):gsub("...", function(cs)
|
||||
local a, b, c = byte(cs, 1, 3)
|
||||
return bs[bit.rshift(a,2)] .. bs[bit.bor(bit.lshift(bit.band(a,3),4),bit.rshift(b,4))] .. bs[bit.bor(bit.lshift(bit.band(b,15),2),bit.rshift(c,6))] .. bs[bit.band(c,63)]
|
||||
end)
|
||||
return s:sub(1, #s-pad) .. rep('=', pad)
|
||||
end
|
||||
function base64.decode(s)
|
||||
local s=s:match("["..s.."=]+")
|
||||
local p,cc=s:gsub("=","A")
|
||||
local r=""
|
||||
local n=0
|
||||
s=s:sub(1,#s-#p)..p
|
||||
for c = 1,#s,4 do
|
||||
n = bit.lshift(bsd[s:sub(c, c)], 18) + bit.lshift(bsd[s:sub(c+1, c+1)], 12) + bit.lshift(bsd[s:sub(c + 2, c + 2)], 6) + bsd[s:sub(c + 3, c + 3)]
|
||||
r = r .. char(bit.band(bit.arshift(n, 16), 0xFF)) .. char(bit.band(bit.arshift(n, 8), 0xFF)) .. char(bit.band(n, 0xFF))
|
||||
end
|
||||
return r:sub(1,-(cc+1))
|
||||
end
|
||||
return base64
|
||||
72
examples/love2d Threading Example/bin/converters/base91.lua
Normal file
72
examples/love2d Threading Example/bin/converters/base91.lua
Normal file
@ -0,0 +1,72 @@
|
||||
local base91={}
|
||||
local b91enc={[0]=
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
|
||||
'%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
|
||||
'>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"'
|
||||
}
|
||||
local b91dec=table.flip(b91enc)
|
||||
function base91.decode(d)
|
||||
local l,v,o,b,n = #d,-1,"",0,0
|
||||
for i in d:gmatch(".") do
|
||||
local c=b91dec[i]
|
||||
if not(c) then
|
||||
-- Continue
|
||||
else
|
||||
if v < 0 then
|
||||
v = c
|
||||
else
|
||||
v = v+c*91
|
||||
b = bit.bor(b, bit.lshift(v,n))
|
||||
if bit.band(v,8191) then
|
||||
n = n + 13
|
||||
else
|
||||
n = n + 14
|
||||
end
|
||||
while true do
|
||||
o=o..string.char(bit.band(b,255))
|
||||
b=bit.rshift(b,8)
|
||||
n=n-8
|
||||
if not (n>7) then
|
||||
break
|
||||
end
|
||||
end
|
||||
v=-1
|
||||
end
|
||||
end
|
||||
end
|
||||
if v + 1>0 then
|
||||
o=o..string.char(bit.band(bit.bor(b,bit.lshift(v,n)),255))
|
||||
end
|
||||
return o
|
||||
end
|
||||
function base91.encode(d)
|
||||
local b,n,o,l=0,0,"",#d
|
||||
for i in d:gmatch(".") do
|
||||
b=bit.bor(b,bit.lshift(string.byte(i),n))
|
||||
n=n+8
|
||||
if n>13 then
|
||||
v=bit.band(b,8191)
|
||||
if v>88 then
|
||||
b=bit.rshift(b,13)
|
||||
n=n-13
|
||||
else
|
||||
v=bit.band(b,16383)
|
||||
b=bit.rshift(b,14)
|
||||
n=n-14
|
||||
end
|
||||
o=o..b91enc[v % 91] .. b91enc[math.floor(v / 91)]
|
||||
end
|
||||
end
|
||||
if n>0 then
|
||||
o=o..b91enc[b % 91]
|
||||
if n>7 or b>90 then
|
||||
o=o .. b91enc[math.floor(b / 91)]
|
||||
end
|
||||
end
|
||||
return o
|
||||
end
|
||||
return base91
|
||||
377
examples/love2d Threading Example/bin/hashes/md5.lua
Normal file
377
examples/love2d Threading Example/bin/hashes/md5.lua
Normal file
@ -0,0 +1,377 @@
|
||||
local md5 = {
|
||||
_VERSION = "md5.lua 1.1.0",
|
||||
_DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
|
||||
_URL = "https://github.com/kikito/md5.lua",
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
|
||||
]]
|
||||
}
|
||||
|
||||
-- bit lib implementions
|
||||
|
||||
local char, byte, format, rep, sub =
|
||||
string.char, string.byte, string.format, string.rep, string.sub
|
||||
local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
|
||||
|
||||
local ok, bit = pcall(require, 'bit')
|
||||
if ok then
|
||||
bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift
|
||||
else
|
||||
ok, bit = pcall(require, 'bit32')
|
||||
|
||||
if ok then
|
||||
|
||||
bit_not = bit.bnot
|
||||
|
||||
local tobit = function(n)
|
||||
return n <= 0x7fffffff and n or -(bit_not(n) + 1)
|
||||
end
|
||||
|
||||
local normalize = function(f)
|
||||
return function(a,b) return tobit(f(tobit(a), tobit(b))) end
|
||||
end
|
||||
|
||||
bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
|
||||
bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
|
||||
|
||||
else
|
||||
|
||||
local function tbl2number(tbl)
|
||||
local result = 0
|
||||
local power = 1
|
||||
for i = 1, #tbl do
|
||||
result = result + tbl[i] * power
|
||||
power = power * 2
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function expand(t1, t2)
|
||||
local big, small = t1, t2
|
||||
if(#big < #small) then
|
||||
big, small = small, big
|
||||
end
|
||||
-- expand small
|
||||
for i = #small + 1, #big do
|
||||
small[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
local to_bits -- needs to be declared before bit_not
|
||||
|
||||
bit_not = function(n)
|
||||
local tbl = to_bits(n)
|
||||
local size = math.max(#tbl, 32)
|
||||
for i = 1, size do
|
||||
if(tbl[i] == 1) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
-- defined as local above
|
||||
to_bits = function (n)
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
return to_bits(bit_not(math.abs(n)) + 1)
|
||||
end
|
||||
-- to bits table
|
||||
local tbl = {}
|
||||
local cnt = 1
|
||||
local last
|
||||
while n > 0 do
|
||||
last = n % 2
|
||||
tbl[cnt] = last
|
||||
n = (n-last)/2
|
||||
cnt = cnt + 1
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
bit_or = function(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
for i = 1, #tbl_m do
|
||||
if(tbl_m[i]== 0 and tbl_n[i] == 0) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
bit_and = function(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
for i = 1, #tbl_m do
|
||||
if(tbl_m[i]== 0 or tbl_n[i] == 0) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
bit_xor = function(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
for i = 1, #tbl_m do
|
||||
if(tbl_m[i] ~= tbl_n[i]) then
|
||||
tbl[i] = 1
|
||||
else
|
||||
tbl[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
bit_rshift = function(n, bits)
|
||||
local high_bit = 0
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
n = bit_not(math.abs(n)) + 1
|
||||
high_bit = 0x80000000
|
||||
end
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
for i=1, bits do
|
||||
n = n/2
|
||||
n = bit_or(floor(n), high_bit)
|
||||
end
|
||||
return floor(n)
|
||||
end
|
||||
|
||||
bit_lshift = function(n, bits)
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
n = bit_not(math.abs(n)) + 1
|
||||
end
|
||||
|
||||
for i=1, bits do
|
||||
n = n*2
|
||||
end
|
||||
return bit_and(n, 0xFFFFFFFF)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- convert little-endian 32-bit int to a 4-char string
|
||||
local function lei2str(i)
|
||||
local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
|
||||
return f(0)..f(8)..f(16)..f(24)
|
||||
end
|
||||
|
||||
-- convert raw string to big-endian int
|
||||
local function str2bei(s)
|
||||
local v=0
|
||||
for i=1, #s do
|
||||
v = v * 256 + byte(s, i)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- convert raw string to little-endian int
|
||||
local function str2lei(s)
|
||||
local v=0
|
||||
for i = #s,1,-1 do
|
||||
v = v*256 + byte(s, i)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- cut up a string in little-endian ints of given size
|
||||
local function cut_le_str(s,...)
|
||||
local o, r = 1, {}
|
||||
local args = {...}
|
||||
for i=1, #args do
|
||||
table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
|
||||
o = o + args[i]
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
local swap = function (w) return str2bei(lei2str(w)) end
|
||||
|
||||
-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
|
||||
-- 10/02/2001 jcw@equi4.com
|
||||
|
||||
local CONSTS = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
|
||||
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
|
||||
}
|
||||
|
||||
local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
|
||||
local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
|
||||
local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
|
||||
local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
|
||||
local z=function (ff,a,b,c,d,x,s,ac)
|
||||
a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF)
|
||||
-- be *very* careful that left shift does not cause rounding!
|
||||
return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b
|
||||
end
|
||||
|
||||
local function transform(A,B,C,D,X)
|
||||
local a,b,c,d=A,B,C,D
|
||||
local t=CONSTS
|
||||
|
||||
a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
|
||||
d=z(f,d,a,b,c,X[ 1],12,t[ 2])
|
||||
c=z(f,c,d,a,b,X[ 2],17,t[ 3])
|
||||
b=z(f,b,c,d,a,X[ 3],22,t[ 4])
|
||||
a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
|
||||
d=z(f,d,a,b,c,X[ 5],12,t[ 6])
|
||||
c=z(f,c,d,a,b,X[ 6],17,t[ 7])
|
||||
b=z(f,b,c,d,a,X[ 7],22,t[ 8])
|
||||
a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
|
||||
d=z(f,d,a,b,c,X[ 9],12,t[10])
|
||||
c=z(f,c,d,a,b,X[10],17,t[11])
|
||||
b=z(f,b,c,d,a,X[11],22,t[12])
|
||||
a=z(f,a,b,c,d,X[12], 7,t[13])
|
||||
d=z(f,d,a,b,c,X[13],12,t[14])
|
||||
c=z(f,c,d,a,b,X[14],17,t[15])
|
||||
b=z(f,b,c,d,a,X[15],22,t[16])
|
||||
|
||||
a=z(g,a,b,c,d,X[ 1], 5,t[17])
|
||||
d=z(g,d,a,b,c,X[ 6], 9,t[18])
|
||||
c=z(g,c,d,a,b,X[11],14,t[19])
|
||||
b=z(g,b,c,d,a,X[ 0],20,t[20])
|
||||
a=z(g,a,b,c,d,X[ 5], 5,t[21])
|
||||
d=z(g,d,a,b,c,X[10], 9,t[22])
|
||||
c=z(g,c,d,a,b,X[15],14,t[23])
|
||||
b=z(g,b,c,d,a,X[ 4],20,t[24])
|
||||
a=z(g,a,b,c,d,X[ 9], 5,t[25])
|
||||
d=z(g,d,a,b,c,X[14], 9,t[26])
|
||||
c=z(g,c,d,a,b,X[ 3],14,t[27])
|
||||
b=z(g,b,c,d,a,X[ 8],20,t[28])
|
||||
a=z(g,a,b,c,d,X[13], 5,t[29])
|
||||
d=z(g,d,a,b,c,X[ 2], 9,t[30])
|
||||
c=z(g,c,d,a,b,X[ 7],14,t[31])
|
||||
b=z(g,b,c,d,a,X[12],20,t[32])
|
||||
|
||||
a=z(h,a,b,c,d,X[ 5], 4,t[33])
|
||||
d=z(h,d,a,b,c,X[ 8],11,t[34])
|
||||
c=z(h,c,d,a,b,X[11],16,t[35])
|
||||
b=z(h,b,c,d,a,X[14],23,t[36])
|
||||
a=z(h,a,b,c,d,X[ 1], 4,t[37])
|
||||
d=z(h,d,a,b,c,X[ 4],11,t[38])
|
||||
c=z(h,c,d,a,b,X[ 7],16,t[39])
|
||||
b=z(h,b,c,d,a,X[10],23,t[40])
|
||||
a=z(h,a,b,c,d,X[13], 4,t[41])
|
||||
d=z(h,d,a,b,c,X[ 0],11,t[42])
|
||||
c=z(h,c,d,a,b,X[ 3],16,t[43])
|
||||
b=z(h,b,c,d,a,X[ 6],23,t[44])
|
||||
a=z(h,a,b,c,d,X[ 9], 4,t[45])
|
||||
d=z(h,d,a,b,c,X[12],11,t[46])
|
||||
c=z(h,c,d,a,b,X[15],16,t[47])
|
||||
b=z(h,b,c,d,a,X[ 2],23,t[48])
|
||||
|
||||
a=z(i,a,b,c,d,X[ 0], 6,t[49])
|
||||
d=z(i,d,a,b,c,X[ 7],10,t[50])
|
||||
c=z(i,c,d,a,b,X[14],15,t[51])
|
||||
b=z(i,b,c,d,a,X[ 5],21,t[52])
|
||||
a=z(i,a,b,c,d,X[12], 6,t[53])
|
||||
d=z(i,d,a,b,c,X[ 3],10,t[54])
|
||||
c=z(i,c,d,a,b,X[10],15,t[55])
|
||||
b=z(i,b,c,d,a,X[ 1],21,t[56])
|
||||
a=z(i,a,b,c,d,X[ 8], 6,t[57])
|
||||
d=z(i,d,a,b,c,X[15],10,t[58])
|
||||
c=z(i,c,d,a,b,X[ 6],15,t[59])
|
||||
b=z(i,b,c,d,a,X[13],21,t[60])
|
||||
a=z(i,a,b,c,d,X[ 4], 6,t[61])
|
||||
d=z(i,d,a,b,c,X[11],10,t[62])
|
||||
c=z(i,c,d,a,b,X[ 2],15,t[63])
|
||||
b=z(i,b,c,d,a,X[ 9],21,t[64])
|
||||
|
||||
return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF),
|
||||
bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF)
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
local function md5_update(self, s)
|
||||
self.pos = self.pos + #s
|
||||
s = self.buf .. s
|
||||
for ii = 1, #s - 63, 64 do
|
||||
local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
|
||||
assert(#X == 16)
|
||||
X[0] = table.remove(X,1) -- zero based!
|
||||
self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X)
|
||||
end
|
||||
self.buf = sub(s, math.floor(#s/64)*64 + 1, #s)
|
||||
return self
|
||||
end
|
||||
|
||||
local function md5_finish(self)
|
||||
local msgLen = self.pos
|
||||
local padLen = 56 - msgLen % 64
|
||||
|
||||
if msgLen % 64 > 56 then padLen = padLen + 64 end
|
||||
|
||||
if padLen == 0 then padLen = 64 end
|
||||
|
||||
local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000))
|
||||
md5_update(self, s)
|
||||
|
||||
assert(self.pos % 64 == 0)
|
||||
return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d)
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
function md5.new()
|
||||
return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68],
|
||||
pos = 0,
|
||||
buf = '',
|
||||
update = md5_update,
|
||||
finish = md5_finish }
|
||||
end
|
||||
|
||||
function md5.tohex(s)
|
||||
return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16)))
|
||||
end
|
||||
|
||||
function md5.sum(s)
|
||||
return md5.new():update(s):finish()
|
||||
end
|
||||
|
||||
function md5.sumhexa(s)
|
||||
return md5.tohex(md5.sum(s))
|
||||
end
|
||||
|
||||
return md5
|
||||
739
examples/love2d Threading Example/bin/init.lua
Normal file
739
examples/love2d Threading Example/bin/init.lua
Normal file
@ -0,0 +1,739 @@
|
||||
bin={}
|
||||
bin.Version={5,1,0}
|
||||
bin.stage='stable'
|
||||
bin.data=''
|
||||
bin.t='bin'
|
||||
bin.__index = bin
|
||||
bin.__tostring=function(self) return self:getData() end
|
||||
bin.__len=function(self) return self:getlength() end
|
||||
bin.lastBlockSize=0
|
||||
bin.streams={}
|
||||
-- Helpers
|
||||
function bin.getVersion()
|
||||
return bin.Version[1]..'.'..bin.Version[2]..'.'..bin.Version[3]
|
||||
end
|
||||
require("bin.support.utils")
|
||||
if jit then
|
||||
bit=require("bit")
|
||||
elseif bit32 then
|
||||
bit=bit32
|
||||
else
|
||||
bit=require("bin.numbers.no_jit_bit")
|
||||
end
|
||||
base64=require("bin.converters.base64")
|
||||
base91=require("bin.converters.base91")
|
||||
bin.lzw=require("bin.compressors.lzw") -- A WIP
|
||||
bits=require("bin.numbers.bits")
|
||||
infinabits=require("bin.numbers.infinabits") -- like the bits library but works past 32 bits for 32bit lua and 64 bits for 64 bit lua.
|
||||
bin.md5=require("bin.hashes.md5")
|
||||
randomGen=require("bin.numbers.random")
|
||||
function bin.setBitsInterface(int)
|
||||
bin.defualtBit=int or bits
|
||||
end
|
||||
bin.setBitsInterface()
|
||||
function bin.normalizeData(data) -- unified function to allow for all types to string
|
||||
if type(data)=="string" then return data end
|
||||
if type(data)=="table" then
|
||||
if data.Type=="bin" or data.Type=="streamable" or data.Type=="buffer" then
|
||||
return data:getData()
|
||||
elseif data.Type=="bits" or data.Type=="infinabits" then
|
||||
return data:toSbytes()
|
||||
elseif data.Type=="sink" then
|
||||
-- LATER
|
||||
else
|
||||
return ""
|
||||
end
|
||||
elseif type(data)=="userdata" then
|
||||
if tostring(data):sub(1,4)=="file" then
|
||||
local cur=data:seek("cur")
|
||||
data:seek("set",0)
|
||||
local dat=data:read("*a")
|
||||
data:seek("set",cur)
|
||||
return dat
|
||||
else
|
||||
error("File handles are the only userdata that can be used!")
|
||||
end
|
||||
end
|
||||
end
|
||||
function bin.resolveType(tab) -- used in getblock for auto object creation. Internal method
|
||||
if tab.Type then
|
||||
if tab.Type=="bin" then
|
||||
return bin.new(tab.data)
|
||||
elseif tab.Type=="streamable" then
|
||||
if bin.fileExist(tab.file) then return nil,"Cannot load the stream file, source file does not exist!" end
|
||||
return bin.stream(tab.file,tab.lock)
|
||||
elseif tab.Type=="buffer" then
|
||||
local buf=bin.newDataBuffer(tab.size)
|
||||
buf[1]=tab:getData()
|
||||
return buf
|
||||
elseif tab.Type=="bits" then
|
||||
local b=bits.new("")
|
||||
b.data=tab.data
|
||||
return b
|
||||
elseif tab.Type=="infinabits" then
|
||||
local b=infinabits.new("")
|
||||
b.data=tab.data
|
||||
return b
|
||||
elseif tab.Type=="sink" then
|
||||
return bin.newSync(tab.data)
|
||||
else -- maybe a type from another library
|
||||
return tab
|
||||
end
|
||||
else return tab end
|
||||
end
|
||||
function bin.fileExist(path)
|
||||
g=io.open(path or '','r')
|
||||
if path =='' then
|
||||
p='empty path'
|
||||
return nil
|
||||
end
|
||||
if g~=nil and true or false then
|
||||
p=(g~=nil and true or false)
|
||||
end
|
||||
if g~=nil then
|
||||
io.close(g)
|
||||
else
|
||||
return false
|
||||
end
|
||||
return p
|
||||
end
|
||||
function bin.toHex(str)
|
||||
local str=bin.normalizeData(str)
|
||||
return (str:gsub('.', function (c)
|
||||
return string.format('%02X', string.byte(c))
|
||||
end))
|
||||
end
|
||||
function bin.fromHex(str)
|
||||
return (str:gsub('..', function (cc)
|
||||
return string.char(tonumber(cc, 16))
|
||||
end))
|
||||
end
|
||||
function bin.toBase64(s)
|
||||
return base64.encode(s)
|
||||
end
|
||||
function bin.fromBase64(s)
|
||||
return base64.decode(s)
|
||||
end
|
||||
function bin.toBase91(s)
|
||||
return base91.encode(s)
|
||||
end
|
||||
function bin.fromBase91(s)
|
||||
return base91.decode(s)
|
||||
end
|
||||
-- Constructors
|
||||
function bin.new(data)
|
||||
data=bin.normalizeData(data)
|
||||
local c = {}
|
||||
setmetatable(c, bin)
|
||||
c.data=data
|
||||
c.Type="bin"
|
||||
c.t="bin"
|
||||
c.pos=1
|
||||
c.stream=false
|
||||
return c
|
||||
end
|
||||
function bin.newFromBase64(data)
|
||||
return bin.new(bin.fromBase64(data))
|
||||
end
|
||||
function bin.newFromBase91(data)
|
||||
return bin.new(bin.fromBase91(data))
|
||||
end
|
||||
function bin.newFromHex(data)
|
||||
return bin.new(bin.fromHex(data))
|
||||
end
|
||||
function bin.load(path)
|
||||
if type(path) ~= "string" then error("Path must be a string!") end
|
||||
local f = io.open(path, 'rb')
|
||||
local content = f:read('*a')
|
||||
f:close()
|
||||
return bin.new(content)
|
||||
end
|
||||
function bin.stream(file,l)
|
||||
if not(l==false) then l=true end
|
||||
local c=bin.new()
|
||||
c.Type="streamable"
|
||||
c.t="streamable"
|
||||
if bin.streams[file]~=nil then
|
||||
c.file=file
|
||||
c.lock = l
|
||||
c.workingfile=bin.streams[file][1].workingfile
|
||||
bin.streams[file][2]=bin.streams[file][2]+1
|
||||
c.stream=true
|
||||
return c
|
||||
end
|
||||
if bin.fileExist(file) then
|
||||
c.file=file
|
||||
c.lock = l
|
||||
c.workingfile=io.open(file,'rb+')
|
||||
else
|
||||
c.file=file
|
||||
c.lock = l
|
||||
c.workingfile=io.open(file,'w')
|
||||
io.close(c.workingfile)
|
||||
c.workingfile=io.open(file,'rb+')
|
||||
end
|
||||
c.stream=true
|
||||
bin.streams[file]={c,1}
|
||||
return c
|
||||
end
|
||||
function bin.newTempFile()
|
||||
local c=bin.new()
|
||||
c.file=file
|
||||
c.lock = false
|
||||
c.workingfile=io.tmpfile()
|
||||
c.stream=true
|
||||
return c
|
||||
end
|
||||
function bin.freshStream(file)
|
||||
bin.new():tofile(file)
|
||||
return bin.stream(file,false)
|
||||
end
|
||||
function bin.newStreamFileObject(file)
|
||||
local c=bin.new()
|
||||
c.Type="streamable"
|
||||
c.t="streamable"
|
||||
c.file="FILE_OBJECT"
|
||||
c.lock = false
|
||||
c.workingfile=file
|
||||
c.stream=true
|
||||
return c
|
||||
end
|
||||
-- Core Methods
|
||||
function bin:canStreamWrite()
|
||||
return (self.stream and not(self.lock))
|
||||
end
|
||||
function bin:getSeek()
|
||||
if self.stream then
|
||||
return self.workingfile:seek("cur")+1
|
||||
else
|
||||
return self.pos
|
||||
end
|
||||
end
|
||||
function bin:setSeek(n)
|
||||
if self.stream then
|
||||
self.workingfile:seek("set",n-1)
|
||||
else
|
||||
self.pos=n
|
||||
end
|
||||
end
|
||||
function bin:seek(n)
|
||||
if self.stream then
|
||||
if not n then return self.workingfile:seek("cur") end
|
||||
local cur=self.workingfile:seek("cur")
|
||||
self.workingfile:seek("set",cur+n)
|
||||
else
|
||||
if not n then return self.pos end
|
||||
if #self.data-(self.pos-1)<n then
|
||||
print(n-((#self.data)-(self.pos-1)))
|
||||
self:write(string.rep("\0",n-((#self.data)-(self.pos-1))))
|
||||
return
|
||||
end
|
||||
self.pos=self.pos+n
|
||||
end
|
||||
end
|
||||
function bin:read(n)
|
||||
if self.stream then
|
||||
return self.workingfile:read(n)
|
||||
else
|
||||
local data=self.data:sub(self.pos,self.pos+n-1)
|
||||
self.pos=self.pos+n
|
||||
if data=="" then return end
|
||||
return data
|
||||
end
|
||||
end
|
||||
function bin:write(data,size)
|
||||
local data=bin.normalizeData(data)
|
||||
local dsize=#data
|
||||
local size=tonumber(size or dsize)
|
||||
if dsize>size then
|
||||
data = data:sub(1,size)
|
||||
elseif dsize<size then
|
||||
data=data..string.rep("\0",size-dsize)
|
||||
end
|
||||
if self:canStreamWrite() then
|
||||
self.workingfile:write(data)
|
||||
elseif self.Type=="bin" then
|
||||
local tab={}
|
||||
if self.pos==1 then
|
||||
tab={data,self.data:sub(self.pos+size)}
|
||||
else
|
||||
tab={self.data:sub(1,self.pos-1),data,self.data:sub(self.pos+size)}
|
||||
end
|
||||
self.pos=self.pos+size
|
||||
self.data=table.concat(tab)
|
||||
else
|
||||
error("Attempted to write to a locked file!")
|
||||
end
|
||||
end
|
||||
function bin:sub(a,b)
|
||||
local data=""
|
||||
if self.stream then
|
||||
local cur=self.workingfile:seek("cur")
|
||||
self.workingfile:seek("set",a-1)
|
||||
data=self.workingfile:read(b-(a-1))
|
||||
self.workingfile:seek("set",cur)
|
||||
else
|
||||
data=self.data:sub(a,b)
|
||||
end
|
||||
return data
|
||||
end
|
||||
function bin:slide(n)
|
||||
local s=self:getSize()
|
||||
local buf=bin.newDataBuffer(s)
|
||||
buf:fillBuffer(1,self:getData())
|
||||
for i=1,s do
|
||||
nn=buf[i]+n
|
||||
if nn>255 then
|
||||
nn=nn%256
|
||||
elseif nn<0 then
|
||||
nn=256-math.abs(nn)
|
||||
end
|
||||
buf[i]=nn
|
||||
end
|
||||
self:setSeek(1)
|
||||
self:write(buf:getData())
|
||||
end
|
||||
function bin:getData(a,b,fmt)
|
||||
local data=""
|
||||
if a or b then
|
||||
data=self:sub(a,b)
|
||||
else
|
||||
if self.stream then
|
||||
local cur=self.workingfile:seek("cur")
|
||||
self.workingfile:seek("set",0)
|
||||
data=self.workingfile:read("*a")
|
||||
self.workingfile:seek("set",cur)
|
||||
else
|
||||
data=self.data
|
||||
end
|
||||
end
|
||||
if fmt=="%x" or fmt=="hex" then
|
||||
return bin.toHex(data):lower()
|
||||
elseif fmt=="%X" or fmt=="HEX" then
|
||||
return bin.toHex(data)
|
||||
elseif fmt=="%b" or fmt=="b64" then
|
||||
return bin.toB64(data)
|
||||
elseif fmt then
|
||||
return bin.new(data):getBlock(fmt,#data)
|
||||
end
|
||||
return data
|
||||
end
|
||||
function bin:getSize(fmt)
|
||||
local len=0
|
||||
if self.stream then
|
||||
local cur=self.workingfile:seek("cur")
|
||||
len=self.workingfile:seek("end")
|
||||
self.workingfile:seek("set",cur)
|
||||
else
|
||||
len=#self.data
|
||||
end
|
||||
if fmt=="%b" then
|
||||
return bin.toB64()
|
||||
elseif fmt then
|
||||
return string.format(fmt, len)
|
||||
else
|
||||
return len
|
||||
end
|
||||
end
|
||||
function bin:tackE(data,size,h)
|
||||
local data=bin.normalizeData(data)
|
||||
local cur=self:getSize()
|
||||
self:setSeek(self:getSize()+1)
|
||||
self:write(data,size)
|
||||
if h then
|
||||
self:setSeek(cur+1)
|
||||
end
|
||||
end
|
||||
function bin:tonumber(a,b)
|
||||
local temp={}
|
||||
if a then
|
||||
temp.data=self:sub(a,b)
|
||||
else
|
||||
temp=self
|
||||
end
|
||||
local l,r=0,0
|
||||
local g=#temp.data
|
||||
for i=1,g do
|
||||
r=r+(256^(g-i))*string.byte(string.sub(temp.data,i,i))
|
||||
l=l+(256^(i-1))*string.byte(string.sub(temp.data,i,i))
|
||||
end
|
||||
return r,l
|
||||
end
|
||||
function bin.endianflop(data)
|
||||
return string.reverse(data)
|
||||
end
|
||||
function bin:tofile(name)
|
||||
if self.stream then return end
|
||||
if not name then error("Must include a filename to save as!") end
|
||||
file = io.open(name, "wb")
|
||||
file:write(self.data)
|
||||
file:close()
|
||||
end
|
||||
function bin:close()
|
||||
if self.stream then
|
||||
if bin.streams[self.file][2]==1 then
|
||||
bin.streams[self.file]=nil
|
||||
self.workingfile:close()
|
||||
else
|
||||
bin.streams[self.file][2]=bin.streams[self.file][2]-1
|
||||
self.workingfile=io.tmpfile()
|
||||
self.workingfile:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
function bin:getBlock(t,n)
|
||||
local data=""
|
||||
if not n then
|
||||
if bin.registerBlocks[t] then
|
||||
return bin.registerBlocks[t][1](nil,self)
|
||||
else
|
||||
error("Unknown format! Cannot read from file: "..tostring(t))
|
||||
end
|
||||
else
|
||||
if t=="n" or t=="%e" or t=="%E" then
|
||||
data=self:read(n)
|
||||
local numB=bin.defualtBit.new(data)
|
||||
local numL=bin.defualtBit.new(string.reverse(data))
|
||||
local little=numL:tonumber(0)
|
||||
local big=numB:tonumber(0)
|
||||
if t=="%E" then
|
||||
return big
|
||||
elseif t=="%X" then
|
||||
return bin.toHex(data):upper()
|
||||
elseif t=="%x" then
|
||||
return bin.toHex(data):lower()
|
||||
elseif t=="%b" then
|
||||
return bin.toB64(data)
|
||||
elseif t=="%e" then
|
||||
return little
|
||||
end
|
||||
return big,little
|
||||
elseif t=="s" then
|
||||
return self:read(n)
|
||||
elseif bin.registerBlocks[t] then
|
||||
return bin.registerBlocks[t][1](n,self)
|
||||
else
|
||||
error("Unknown format! Cannot read from file: "..tostring(t))
|
||||
end
|
||||
end
|
||||
end
|
||||
function bin:addBlock(d,fit,fmt)
|
||||
if not fmt then fmt=type(d):sub(1,1) end
|
||||
if bin.registerBlocks[fmt] then
|
||||
self:tackE(bin.registerBlocks[fmt][2](d,fit,fmt,self,bin.registerBlocks[fmt][2]))
|
||||
elseif type(d)=="number" then
|
||||
local data=bin.defualtBit.numToBytes(d,fit or 4,fmt,function()
|
||||
error("Overflow! Space allotted for number is smaller than the number takes up. Increase the fit!")
|
||||
end)
|
||||
self:tackE(data)
|
||||
elseif type(d)=="string" then
|
||||
local data=d:sub(1,fit or -1)
|
||||
if #data<(fit or #data) then
|
||||
data=data..string.rep("\0",fit-#data)
|
||||
end
|
||||
self:tackE(data)
|
||||
end
|
||||
end
|
||||
bin.registerBlocks={}
|
||||
function bin.registerBlock(t,funcG,funcA)
|
||||
bin.registerBlocks[t]={funcG,funcA}
|
||||
end
|
||||
function bin.newDataBuffer(size,fill) -- fills with \0 or nul or with what you enter
|
||||
local c={}
|
||||
local fill=fill or "\0"
|
||||
c.data={self=c}
|
||||
c.Type="buffer"
|
||||
c.size=size or 0 -- 0 means an infinite buffer, sometimes useful
|
||||
for i=1,c.size do
|
||||
c.data[i]=fill
|
||||
end
|
||||
local mt={
|
||||
__index=function(t,k)
|
||||
if type(k)=="number" then
|
||||
local data=t.data[k]
|
||||
if data then
|
||||
return string.byte(data)
|
||||
else
|
||||
error("Index out of range!")
|
||||
end
|
||||
elseif type(k)=="string" then
|
||||
local num=tonumber(k)
|
||||
if num then
|
||||
local data=t.data[num]
|
||||
if data then
|
||||
return data
|
||||
else
|
||||
error("Index out of range!")
|
||||
end
|
||||
else
|
||||
error("Only number-strings and numbers can be indexed!")
|
||||
end
|
||||
else
|
||||
error("Only number-strings and numbers can be indexed!")
|
||||
end
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
if type(k)~="number" then error("Can only set a buffers data with a numeric index!") end
|
||||
local data=""
|
||||
if type(v)=="string" then
|
||||
data=v
|
||||
elseif type(v)=="number" then
|
||||
data=string.char(v)
|
||||
else
|
||||
-- try to normalize the data of type v
|
||||
data=bin.normalizeData(v)
|
||||
end
|
||||
t:fillBuffer(k,data)
|
||||
end,
|
||||
__tostring=function(t)
|
||||
return t:getData()
|
||||
end,
|
||||
}
|
||||
function c:fillBuffer(a,data)
|
||||
local len=#data
|
||||
if len==1 then
|
||||
self.data[a]=data
|
||||
else
|
||||
local i=a-1
|
||||
for d in data:gmatch(".") do
|
||||
i=i+1
|
||||
if i>c.size then
|
||||
return #data-i+a
|
||||
end
|
||||
self.data[i]=d
|
||||
end
|
||||
return #data-i+(a-1)
|
||||
end
|
||||
end
|
||||
function c:getData(a,b,fmt) -- LATER
|
||||
local dat=bin.new(table.concat(self.data,"",a,b))
|
||||
local n=dat:getSize()
|
||||
return dat:getBlock(fmt or "s",n)
|
||||
end
|
||||
function c:getSize()
|
||||
return #self:getData()
|
||||
end
|
||||
setmetatable(c,mt)
|
||||
return c
|
||||
end
|
||||
function bin:newDataBufferFromStream(pos,size,fill) -- fills with \0 or nul or with what you enter IF the nothing exists inside the bin file.
|
||||
local s=self:getSize()
|
||||
if not self.stream then error("Can only created a streamed buffer on a streamable file!") end
|
||||
if s==0 then
|
||||
self:write(string.rep("\0",pos+size))
|
||||
end
|
||||
self:setSeek(1)
|
||||
local c=bin.newDataBuffer(size,fill)
|
||||
rawset(c,"pos",pos)
|
||||
rawset(c,"size",size)
|
||||
rawset(c,"fill",fill)
|
||||
rawset(c,"bin",self)
|
||||
rawset(c,"sync",function(self)
|
||||
local cur=self.bin:getSeek()
|
||||
self.bin:setSeek(self.pos)
|
||||
self.bin:write(self:getData(),size)
|
||||
self.bin:setSeek(cur)
|
||||
end)
|
||||
c:fillBuffer(1,self:sub(pos,pos+size))
|
||||
function c:fillBuffer(a,data)
|
||||
local len=#data
|
||||
if len==1 then
|
||||
self.data[a]=data
|
||||
self:sync()
|
||||
else
|
||||
local i=a-1
|
||||
for d in data:gmatch(".") do
|
||||
i=i+1
|
||||
if i>c.size then
|
||||
self:sync()
|
||||
return #data-i+a
|
||||
end
|
||||
self.data[i]=d
|
||||
end
|
||||
self:sync()
|
||||
return #data-i+(a-1)
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
function bin:toDataBuffer()
|
||||
local s=self:getSize()
|
||||
-- if self:canStreamWrite() then
|
||||
-- return self:newDataBufferFromStream(0,s)
|
||||
-- end
|
||||
local buf=bin.newDataBuffer(s)
|
||||
local data=self:read(512)
|
||||
local i=1
|
||||
while data~=nil do
|
||||
buf[i]=data
|
||||
data=self:read(512)
|
||||
i=i+512
|
||||
end
|
||||
return buf
|
||||
end
|
||||
function bin:getMD5Hash()
|
||||
self:setSeek(1)
|
||||
local len=self:getSize()
|
||||
local md5=bin.md5.new()
|
||||
local SIZE=2048
|
||||
if len>SIZE then
|
||||
local dat=self:read(SIZE)
|
||||
while dat~=nil do
|
||||
md5:update(dat)
|
||||
dat=self:read(SIZE)
|
||||
end
|
||||
return bin.md5.tohex(md5:finish()):upper()
|
||||
else
|
||||
return bin.md5.sumhexa(self:getData()):upper()
|
||||
end
|
||||
end
|
||||
function bin:getHash()
|
||||
if self:getSize()==0 then
|
||||
return "NaN"
|
||||
end
|
||||
n=32
|
||||
local rand = randomGen:newND(1,self:getSize(),self:getSize())
|
||||
local h,g={},0
|
||||
for i=1,n do
|
||||
g=rand:nextInt()
|
||||
table.insert(h,bin.toHex(self:sub(g,g)))
|
||||
end
|
||||
return table.concat(h,'')
|
||||
end
|
||||
function bin:flipbits()
|
||||
if self:canStreamWrite() then
|
||||
self:setSeek(1)
|
||||
for i=1,self:getSize() do
|
||||
self:write(string.char(255-string.byte(self:sub(i,i))))
|
||||
end
|
||||
else
|
||||
local temp={}
|
||||
for i=1,#self.data do
|
||||
table.insert(temp,string.char(255-string.byte(string.sub(self.data,i,i))))
|
||||
end
|
||||
self.data=table.concat(temp,'')
|
||||
end
|
||||
end
|
||||
function bin:encrypt()
|
||||
self:flipbits()
|
||||
end
|
||||
function bin:decrypt()
|
||||
self:flipbits()
|
||||
end
|
||||
-- Use with small files!
|
||||
function bin:gsub(...)
|
||||
local data=self:getData()
|
||||
local pos=self:getSeek()
|
||||
self:setSeek(1)
|
||||
self:write((data:gsub(...)) or data)
|
||||
self:setSeek(loc)
|
||||
end
|
||||
function bin:gmatch(pat)
|
||||
return self:getData():gmatch(pat)
|
||||
end
|
||||
function bin:match(pat)
|
||||
return self:getData():match(pat)
|
||||
end
|
||||
function bin:trim()
|
||||
local data=self:getData()
|
||||
local pos=self:getSeek()
|
||||
self:setSeek(1)
|
||||
self:write(data:match'^()%s*$' and '' or data:match'^%s*(.*%S)')
|
||||
self:setSeek(loc)
|
||||
end
|
||||
function bin:lines()
|
||||
local t = {}
|
||||
local function helper(line) table.insert(t, line) return '' end
|
||||
helper((self:getData():gsub('(.-)\r?\n', helper)))
|
||||
return t
|
||||
end
|
||||
function bin._lines(str)
|
||||
local t = {}
|
||||
local function helper(line) table.insert(t, line) return '' end
|
||||
helper((str:gsub('(.-)\r?\n', helper)))
|
||||
return t
|
||||
end
|
||||
function bin:wipe()
|
||||
if self:canStreamWrite() then
|
||||
self:close()
|
||||
local c=bin.freshStream(self.file)
|
||||
self.workingfile=c.workingfile
|
||||
else
|
||||
self.data=""
|
||||
end
|
||||
self:setSeek(1)
|
||||
end
|
||||
function bin:fullTrim(empty)
|
||||
local t=self:lines()
|
||||
for i=#t,1,-1 do
|
||||
t[i]=bin._trim(t[i])
|
||||
if empty then
|
||||
if t[i]=="" then
|
||||
table.remove(t,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
self:wipe()
|
||||
self:write(table.concat(t,"\n"))
|
||||
end
|
||||
require("bin.support.extraBlocks") -- registered blocks that you can use
|
||||
if love then
|
||||
function bin.load(file,s,r)
|
||||
content, size = love.filesystem.read(file)
|
||||
local temp=bin.new(content)
|
||||
temp.filepath=file
|
||||
return temp
|
||||
end
|
||||
function bin:tofile(filename)
|
||||
if not(filename) or self.Stream then return nil end
|
||||
love.filesystem.write(filename,self.data)
|
||||
end
|
||||
function bin.stream(file)
|
||||
return bin.newStreamFileObject(love.filesystem.newFile(file))
|
||||
end
|
||||
function bin:getSize(fmt)
|
||||
local len=0
|
||||
if self.stream then
|
||||
local len=self.workingfile:getSize()
|
||||
else
|
||||
len=#self.data
|
||||
end
|
||||
if fmt=="%b" then
|
||||
return bin.toB64()
|
||||
elseif fmt then
|
||||
return string.format(fmt, len)
|
||||
else
|
||||
return len
|
||||
end
|
||||
end
|
||||
function bin:getSeek()
|
||||
if self.stream then
|
||||
return self.workingfile:tell()+1
|
||||
else
|
||||
return self.pos
|
||||
end
|
||||
end
|
||||
function bin:setSeek(n)
|
||||
if self.stream then
|
||||
self.workingfile:seek(n-1)
|
||||
else
|
||||
self.pos=n
|
||||
end
|
||||
end
|
||||
function bin:seek(n)
|
||||
if self.stream then
|
||||
self.workingfile:seek(n)
|
||||
else
|
||||
if not n then return self.pos end
|
||||
if #self.data-(self.pos-1)<n then
|
||||
print(n-((#self.data)-(self.pos-1)))
|
||||
self:write(string.rep("\0",n-((#self.data)-(self.pos-1))))
|
||||
return
|
||||
end
|
||||
self.pos=self.pos+n
|
||||
end
|
||||
end
|
||||
function bin:close()
|
||||
self.workingfile:close()
|
||||
end
|
||||
end
|
||||
683
examples/love2d Threading Example/bin/numbers/BigNum.lua
Normal file
683
examples/love2d Threading Example/bin/numbers/BigNum.lua
Normal file
@ -0,0 +1,683 @@
|
||||
RADIX = 10^7 ;
|
||||
RADIX_LEN = math.floor( math.log10 ( RADIX ) ) ;
|
||||
|
||||
BigNum = {} ;
|
||||
BigNum.mt = {} ;
|
||||
|
||||
function BigNum.new( num ) --{{{2
|
||||
local bignum = {} ;
|
||||
setmetatable( bignum , BigNum.mt ) ;
|
||||
BigNum.change( bignum , num ) ;
|
||||
return bignum ;
|
||||
end
|
||||
|
||||
function BigNum.mt.sub( num1 , num2 )
|
||||
local temp = BigNum.new() ;
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
BigNum.sub( bnum1 , bnum2 , temp ) ;
|
||||
return temp ;
|
||||
end
|
||||
function BigNum.mt.mod( num1 , num2 )
|
||||
return BigNum.new(num1 - (num1/num2)*num2)
|
||||
end
|
||||
function BigNum.mt.add( num1 , num2 )
|
||||
local temp = BigNum.new() ;
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
BigNum.add( bnum1 , bnum2 , temp ) ;
|
||||
return temp ;
|
||||
end
|
||||
|
||||
function BigNum.mt.mul( num1 , num2 )
|
||||
local temp = BigNum.new() ;
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
BigNum.mul( bnum1 , bnum2 , temp ) ;
|
||||
return temp ;
|
||||
end
|
||||
|
||||
function BigNum.mt.div( num1 , num2 )
|
||||
local bnum1 = {} ;
|
||||
local bnum2 = {} ;
|
||||
local bnum3 = BigNum.new() ;
|
||||
local bnum4 = BigNum.new() ;
|
||||
bnum1 = BigNum.new( num1 ) ;
|
||||
bnum2 = BigNum.new( num2 ) ;
|
||||
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
|
||||
return bnum3 , bnum4 ;
|
||||
end
|
||||
|
||||
function BigNum.mt.tostring( bnum )
|
||||
local i = 0 ;
|
||||
local j = 0 ;
|
||||
local str = "" ;
|
||||
local temp = "" ;
|
||||
if bnum == nil then
|
||||
return "nil" ;
|
||||
elseif bnum.len > 0 then
|
||||
for i = bnum.len - 2 , 0 , -1 do
|
||||
for j = 0 , RADIX_LEN - string.len( bnum[i] ) - 1 do
|
||||
temp = temp .. '0' ;
|
||||
end
|
||||
temp = temp .. bnum[i] ;
|
||||
end
|
||||
if bnum[bnum.len - 1]==nil then
|
||||
return "nil"
|
||||
end
|
||||
temp = bnum[bnum.len - 1] .. temp ;
|
||||
if bnum.signal == '-' then
|
||||
temp = bnum.signal .. temp ;
|
||||
end
|
||||
return temp ;
|
||||
else
|
||||
return "" ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigNum.mt.pow( num1 , num2 )
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
return BigNum.pow( bnum1 , bnum2 ) ;
|
||||
end
|
||||
|
||||
function BigNum.mt.eq( num1 , num2 )
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
return BigNum.eq( bnum1 , bnum2 ) ;
|
||||
end
|
||||
|
||||
function BigNum.mt.lt( num1 , num2 )
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
return BigNum.lt( bnum1 , bnum2 ) ;
|
||||
end
|
||||
|
||||
function BigNum.mt.le( num1 , num2 )
|
||||
local bnum1 = BigNum.new( num1 ) ;
|
||||
local bnum2 = BigNum.new( num2 ) ;
|
||||
return BigNum.le( bnum1 , bnum2 ) ;
|
||||
end
|
||||
|
||||
function BigNum.mt.unm( num )
|
||||
local ret = BigNum.new( num )
|
||||
if ret.signal == '+' then
|
||||
ret.signal = '-'
|
||||
else
|
||||
ret.signal = '+'
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
BigNum.mt.__metatable = "hidden"
|
||||
BigNum.mt.__tostring = BigNum.mt.tostring ;
|
||||
BigNum.mt.__add = BigNum.mt.add ;
|
||||
BigNum.mt.__sub = BigNum.mt.sub ;
|
||||
BigNum.mt.__mul = BigNum.mt.mul ;
|
||||
BigNum.mt.__div = BigNum.mt.div ;
|
||||
BigNum.mt.__pow = BigNum.mt.pow ;
|
||||
BigNum.mt.__unm = BigNum.mt.unm ;
|
||||
BigNum.mt.__mod = BigNum.mt.mod ;
|
||||
BigNum.mt.__eq = BigNum.mt.eq ;
|
||||
BigNum.mt.__le = BigNum.mt.le ;
|
||||
BigNum.mt.__lt = BigNum.mt.lt ;
|
||||
setmetatable( BigNum.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ;
|
||||
function BigNum.add( bnum1 , bnum2 , bnum3 )
|
||||
local maxlen = 0 ;
|
||||
local i = 0 ;
|
||||
local carry = 0 ;
|
||||
local signal = '+' ;
|
||||
local old_len = 0 ;
|
||||
--Handle the signals
|
||||
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
|
||||
error("Function BigNum.add: parameter nil") ;
|
||||
elseif bnum1.signal == '-' and bnum2.signal == '+' then
|
||||
bnum1.signal = '+' ;
|
||||
BigNum.sub( bnum2 , bnum1 , bnum3 ) ;
|
||||
|
||||
if not rawequal(bnum1, bnum3) then
|
||||
bnum1.signal = '-' ;
|
||||
end
|
||||
return 0 ;
|
||||
elseif bnum1.signal == '+' and bnum2.signal == '-' then
|
||||
bnum2.signal = '+' ;
|
||||
BigNum.sub( bnum1 , bnum2 , bnum3 ) ;
|
||||
if not rawequal(bnum2, bnum3) then
|
||||
bnum2.signal = '-' ;
|
||||
end
|
||||
return 0 ;
|
||||
elseif bnum1.signal == '-' and bnum2.signal == '-' then
|
||||
signal = '-' ;
|
||||
end
|
||||
--
|
||||
old_len = bnum3.len ;
|
||||
if bnum1.len > bnum2.len then
|
||||
maxlen = bnum1.len ;
|
||||
else
|
||||
maxlen = bnum2.len ;
|
||||
bnum1 , bnum2 = bnum2 , bnum1 ;
|
||||
end
|
||||
--School grade sum
|
||||
for i = 0 , maxlen - 1 do
|
||||
if bnum2[i] ~= nil then
|
||||
bnum3[i] = bnum1[i] + bnum2[i] + carry ;
|
||||
else
|
||||
bnum3[i] = bnum1[i] + carry ;
|
||||
end
|
||||
if bnum3[i] >= RADIX then
|
||||
bnum3[i] = bnum3[i] - RADIX ;
|
||||
carry = 1 ;
|
||||
else
|
||||
carry = 0 ;
|
||||
end
|
||||
end
|
||||
--Update the answer's size
|
||||
if carry == 1 then
|
||||
bnum3[maxlen] = 1 ;
|
||||
end
|
||||
bnum3.len = maxlen + carry ;
|
||||
bnum3.signal = signal ;
|
||||
for i = bnum3.len, old_len do
|
||||
bnum3[i] = nil ;
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.sub( bnum1 , bnum2 , bnum3 )
|
||||
local maxlen = 0 ;
|
||||
local i = 0 ;
|
||||
local carry = 0 ;
|
||||
local old_len = 0 ;
|
||||
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
|
||||
error("Function BigNum.sub: parameter nil") ;
|
||||
elseif bnum1.signal == '-' and bnum2.signal == '+' then
|
||||
bnum1.signal = '+' ;
|
||||
BigNum.add( bnum1 , bnum2 , bnum3 ) ;
|
||||
bnum3.signal = '-' ;
|
||||
if not rawequal(bnum1, bnum3) then
|
||||
bnum1.signal = '-' ;
|
||||
end
|
||||
return 0 ;
|
||||
elseif bnum1.signal == '-' and bnum2.signal == '-' then
|
||||
bnum1.signal = '+' ;
|
||||
bnum2.signal = '+' ;
|
||||
BigNum.sub( bnum2, bnum1 , bnum3 ) ;
|
||||
if not rawequal(bnum1, bnum3) then
|
||||
bnum1.signal = '-' ;
|
||||
end
|
||||
if not rawequal(bnum2, bnum3) then
|
||||
bnum2.signal = '-' ;
|
||||
end
|
||||
return 0 ;
|
||||
elseif bnum1.signal == '+' and bnum2.signal == '-' then
|
||||
bnum2.signal = '+' ;
|
||||
BigNum.add( bnum1 , bnum2 , bnum3 ) ;
|
||||
if not rawequal(bnum2, bnum3) then
|
||||
bnum2.signal = '-' ;
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
--Tests if bnum2 > bnum1
|
||||
if BigNum.compareAbs( bnum1 , bnum2 ) == 2 then
|
||||
BigNum.sub( bnum2 , bnum1 , bnum3 ) ;
|
||||
bnum3.signal = '-' ;
|
||||
return 0 ;
|
||||
else
|
||||
maxlen = bnum1.len ;
|
||||
end
|
||||
old_len = bnum3.len ;
|
||||
bnum3.len = 0 ;
|
||||
--School grade subtraction
|
||||
for i = 0 , maxlen - 1 do
|
||||
if bnum2[i] ~= nil then
|
||||
bnum3[i] = bnum1[i] - bnum2[i] - carry ;
|
||||
else
|
||||
bnum3[i] = bnum1[i] - carry ;
|
||||
end
|
||||
if bnum3[i] < 0 then
|
||||
bnum3[i] = RADIX + bnum3[i] ;
|
||||
carry = 1 ;
|
||||
else
|
||||
carry = 0 ;
|
||||
end
|
||||
|
||||
if bnum3[i] ~= 0 then
|
||||
bnum3.len = i + 1 ;
|
||||
end
|
||||
end
|
||||
bnum3.signal = '+' ;
|
||||
--Check if answer's size if zero
|
||||
if bnum3.len == 0 then
|
||||
bnum3.len = 1 ;
|
||||
bnum3[0] = 0 ;
|
||||
end
|
||||
if carry == 1 then
|
||||
error( "Error in function sub" ) ;
|
||||
end
|
||||
for i = bnum3.len , max( old_len , maxlen - 1 ) do
|
||||
bnum3[i] = nil ;
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.mul( bnum1 , bnum2 , bnum3 )
|
||||
local i = 0 ; j = 0 ;
|
||||
local temp = BigNum.new( ) ;
|
||||
local temp2 = 0 ;
|
||||
local carry = 0 ;
|
||||
local oldLen = bnum3.len ;
|
||||
if bnum1 == nil or bnum2 == nil or bnum3 == nil then
|
||||
error("Function BigNum.mul: parameter nil") ;
|
||||
--Handle the signals
|
||||
elseif bnum1.signal ~= bnum2.signal then
|
||||
BigNum.mul( bnum1 , -bnum2 , bnum3 ) ;
|
||||
bnum3.signal = '-' ;
|
||||
return 0 ;
|
||||
end
|
||||
bnum3.len = ( bnum1.len ) + ( bnum2.len ) ;
|
||||
--Fill with zeros
|
||||
for i = 1 , bnum3.len do
|
||||
bnum3[i - 1] = 0 ;
|
||||
end
|
||||
--Places nil where passes through this
|
||||
for i = bnum3.len , oldLen do
|
||||
bnum3[i] = nil ;
|
||||
end
|
||||
--School grade multiplication
|
||||
for i = 0 , bnum1.len - 1 do
|
||||
for j = 0 , bnum2.len - 1 do
|
||||
carry = ( bnum1[i] * bnum2[j] + carry ) ;
|
||||
carry = carry + bnum3[i + j] ;
|
||||
bnum3[i + j] = ( carry % RADIX ) ;
|
||||
temp2 = bnum3[i + j] ;
|
||||
carry = math.floor ( carry / RADIX ) ;
|
||||
end
|
||||
if carry ~= 0 then
|
||||
bnum3[i + bnum2.len] = carry ;
|
||||
end
|
||||
carry = 0 ;
|
||||
end
|
||||
|
||||
--Update the answer's size
|
||||
for i = bnum3.len - 1 , 1 , -1 do
|
||||
if bnum3[i] ~= nil and bnum3[i] ~= 0 then
|
||||
break ;
|
||||
else
|
||||
bnum3[i] = nil ;
|
||||
end
|
||||
bnum3.len = bnum3.len - 1 ;
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 )
|
||||
local temp = BigNum.new() ;
|
||||
local temp2 = BigNum.new() ;
|
||||
local one = BigNum.new( "1" ) ;
|
||||
local zero = BigNum.new( "0" ) ;
|
||||
--Check division by zero
|
||||
if BigNum.compareAbs( bnum2 , zero ) == 0 then
|
||||
error( "Function BigNum.div: Division by zero" ) ;
|
||||
end
|
||||
--Handle the signals
|
||||
if bnum1 == nil or bnum2 == nil or bnum3 == nil or bnum4 == nil then
|
||||
error( "Function BigNum.div: parameter nil" ) ;
|
||||
elseif bnum1.signal == "+" and bnum2.signal == "-" then
|
||||
bnum2.signal = "+" ;
|
||||
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
|
||||
bnum2.signal = "-" ;
|
||||
bnum3.signal = "-" ;
|
||||
return 0 ;
|
||||
elseif bnum1.signal == "-" and bnum2.signal == "+" then
|
||||
bnum1.signal = "+" ;
|
||||
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
|
||||
bnum1.signal = "-" ;
|
||||
if bnum4 < zero then --Check if remainder is negative
|
||||
BigNum.add( bnum3 , one , bnum3 ) ;
|
||||
BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
|
||||
end
|
||||
bnum3.signal = "-" ;
|
||||
return 0 ;
|
||||
elseif bnum1.signal == "-" and bnum2.signal == "-" then
|
||||
bnum1.signal = "+" ;
|
||||
bnum2.signal = "+" ;
|
||||
BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ;
|
||||
bnum1.signal = "-" ;
|
||||
if bnum4 < zero then --Check if remainder is negative
|
||||
BigNum.add( bnum3 , one , bnum3 ) ;
|
||||
BigNum.sub( bnum2 , bnum4 , bnum4 ) ;
|
||||
end
|
||||
bnum2.signal = "-" ;
|
||||
return 0 ;
|
||||
end
|
||||
temp.len = bnum1.len - bnum2.len - 1 ;
|
||||
|
||||
--Reset variables
|
||||
BigNum.change( bnum3 , "0" ) ;
|
||||
BigNum.change( bnum4 , "0" ) ;
|
||||
|
||||
BigNum.copy( bnum1 , bnum4 ) ;
|
||||
|
||||
--Check if can continue dividing
|
||||
while( BigNum.compareAbs( bnum4 , bnum2 ) ~= 2 ) do
|
||||
if bnum4[bnum4.len - 1] >= bnum2[bnum2.len - 1] then
|
||||
BigNum.put( temp , math.floor( bnum4[bnum4.len - 1] / bnum2[bnum2.len - 1] ) , bnum4.len - bnum2.len ) ;
|
||||
temp.len = bnum4.len - bnum2.len + 1 ;
|
||||
else
|
||||
BigNum.put( temp , math.floor( ( bnum4[bnum4.len - 1] * RADIX + bnum4[bnum4.len - 2] ) / bnum2[bnum2.len -1] ) , bnum4.len - bnum2.len - 1 ) ;
|
||||
temp.len = bnum4.len - bnum2.len ;
|
||||
end
|
||||
|
||||
if bnum4.signal ~= bnum2.signal then
|
||||
temp.signal = "-";
|
||||
else
|
||||
temp.signal = "+";
|
||||
end
|
||||
BigNum.add( temp , bnum3 , bnum3 ) ;
|
||||
temp = temp * bnum2 ;
|
||||
BigNum.sub( bnum4 , temp , bnum4 ) ;
|
||||
end
|
||||
|
||||
--Update if the remainder is negative
|
||||
if bnum4.signal == '-' then
|
||||
decr( bnum3 ) ;
|
||||
BigNum.add( bnum2 , bnum4 , bnum4 ) ;
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.pow( bnum1 , bnum2 )
|
||||
local n = BigNum.new( bnum2 ) ;
|
||||
local y = BigNum.new( 1 ) ;
|
||||
local z = BigNum.new( bnum1 ) ;
|
||||
local zero = BigNum.new( "0" ) ;
|
||||
if bnum2 < zero then
|
||||
error( "Function BigNum.exp: domain error" ) ;
|
||||
elseif bnum2 == zero then
|
||||
return y ;
|
||||
end
|
||||
while 1 do
|
||||
if ( n[0] % 2 ) == 0 then
|
||||
n = n / 2 ;
|
||||
else
|
||||
n = n / 2 ;
|
||||
y = z * y ;
|
||||
if n == zero then
|
||||
return y ;
|
||||
end
|
||||
end
|
||||
z = z * z ;
|
||||
end
|
||||
end
|
||||
-- Português :
|
||||
BigNum.exp = BigNum.pow
|
||||
|
||||
function BigNum.gcd( bnum1 , bnum2 )
|
||||
local a = {} ;
|
||||
local b = {} ;
|
||||
local c = {} ;
|
||||
local d = {} ;
|
||||
local zero = {} ;
|
||||
zero = BigNum.new( "0" ) ;
|
||||
if bnum1 == zero or bnum2 == zero then
|
||||
return BigNum.new( "1" ) ;
|
||||
end
|
||||
a = BigNum.new( bnum1 ) ;
|
||||
b = BigNum.new( bnum2 ) ;
|
||||
a.signal = '+' ;
|
||||
b.signal = '+' ;
|
||||
c = BigNum.new() ;
|
||||
d = BigNum.new() ;
|
||||
while b > zero do
|
||||
BigNum.div( a , b , c , d ) ;
|
||||
a , b , d = b , d , a ;
|
||||
end
|
||||
return a ;
|
||||
end
|
||||
-- Português:
|
||||
BigNum.mmc = BigNum.gcd
|
||||
|
||||
function BigNum.eq( bnum1 , bnum2 )
|
||||
if BigNum.compare( bnum1 , bnum2 ) == 0 then
|
||||
return true ;
|
||||
else
|
||||
return false ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigNum.lt( bnum1 , bnum2 )
|
||||
if BigNum.compare( bnum1 , bnum2 ) == 2 then
|
||||
return true ;
|
||||
else
|
||||
return false ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigNum.le( bnum1 , bnum2 )
|
||||
local temp = -1 ;
|
||||
temp = BigNum.compare( bnum1 , bnum2 )
|
||||
if temp == 0 or temp == 2 then
|
||||
return true ;
|
||||
else
|
||||
return false ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigNum.compareAbs( bnum1 , bnum2 )
|
||||
if bnum1 == nil or bnum2 == nil then
|
||||
error("Function compare: parameter nil") ;
|
||||
elseif bnum1.len > bnum2.len then
|
||||
return 1 ;
|
||||
elseif bnum1.len < bnum2.len then
|
||||
return 2 ;
|
||||
else
|
||||
local i ;
|
||||
for i = bnum1.len - 1 , 0 , -1 do
|
||||
if bnum1[i] > bnum2[i] then
|
||||
return 1 ;
|
||||
elseif bnum1[i] < bnum2[i] then
|
||||
return 2 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.compare( bnum1 , bnum2 )
|
||||
local signal = 0 ;
|
||||
|
||||
if bnum1 == nil or bnum2 == nil then
|
||||
error("Funtion BigNum.compare: parameter nil") ;
|
||||
elseif bnum1.signal == '+' and bnum2.signal == '-' then
|
||||
return 1 ;
|
||||
elseif bnum1.signal == '-' and bnum2.signal == '+' then
|
||||
return 2 ;
|
||||
elseif bnum1.signal == '-' and bnum2.signal == '-' then
|
||||
signal = 1 ;
|
||||
end
|
||||
if bnum1.len > bnum2.len then
|
||||
return 1 + signal ;
|
||||
elseif bnum1.len < bnum2.len then
|
||||
return 2 - signal ;
|
||||
else
|
||||
local i ;
|
||||
for i = bnum1.len - 1 , 0 , -1 do
|
||||
if bnum1[i] > bnum2[i] then
|
||||
return 1 + signal ;
|
||||
elseif bnum1[i] < bnum2[i] then
|
||||
return 2 - signal ;
|
||||
end
|
||||
end
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.copy( bnum1 , bnum2 )
|
||||
if bnum1 ~= nil and bnum2 ~= nil then
|
||||
local i ;
|
||||
for i = 0 , bnum1.len - 1 do
|
||||
bnum2[i] = bnum1[i] ;
|
||||
end
|
||||
bnum2.len = bnum1.len ;
|
||||
else
|
||||
error("Function BigNum.copy: parameter nil") ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigNum.change( bnum1 , num )
|
||||
local j = 0 ;
|
||||
local len = 0 ;
|
||||
local num = num ;
|
||||
local l ;
|
||||
local oldLen = 0 ;
|
||||
if bnum1 == nil then
|
||||
error( "BigNum.change: parameter nil" ) ;
|
||||
elseif type( bnum1 ) ~= "table" then
|
||||
error( "BigNum.change: parameter error, type unexpected" ) ;
|
||||
elseif num == nil then
|
||||
bnum1.len = 1 ;
|
||||
bnum1[0] = 0 ;
|
||||
bnum1.signal = "+";
|
||||
elseif type( num ) == "table" and num.len ~= nil then --check if num is a big number
|
||||
--copy given table to the new one
|
||||
for i = 0 , num.len do
|
||||
bnum1[i] = num[i] ;
|
||||
end
|
||||
if num.signal ~= '-' and num.signal ~= '+' then
|
||||
bnum1.signal = '+' ;
|
||||
else
|
||||
bnum1.signal = num.signal ;
|
||||
end
|
||||
oldLen = bnum1.len ;
|
||||
bnum1.len = num.len ;
|
||||
elseif type( num ) == "string" or type( num ) == "number" then
|
||||
if string.sub( num , 1 , 1 ) == '+' or string.sub( num , 1 , 1 ) == '-' then
|
||||
bnum1.signal = string.sub( num , 1 , 1 ) ;
|
||||
num = string.sub(num, 2) ;
|
||||
else
|
||||
bnum1.signal = '+' ;
|
||||
end
|
||||
num = string.gsub( num , " " , "" ) ;
|
||||
local sf = string.find( num , "e" ) ;
|
||||
--Handles if the number is in exp notation
|
||||
if sf ~= nil then
|
||||
num = string.gsub( num , "%." , "" ) ;
|
||||
local e = string.sub( num , sf + 1 ) ;
|
||||
e = tonumber(e) ;
|
||||
if e ~= nil and e > 0 then
|
||||
e = tonumber(e) ;
|
||||
else
|
||||
error( "Function BigNum.change: string is not a valid number" ) ;
|
||||
end
|
||||
num = string.sub( num , 1 , sf - 2 ) ;
|
||||
for i = string.len( num ) , e do
|
||||
num = num .. "0" ;
|
||||
end
|
||||
else
|
||||
sf = string.find( num , "%." ) ;
|
||||
if sf ~= nil then
|
||||
num = string.sub( num , 1 , sf - 1 ) ;
|
||||
end
|
||||
end
|
||||
|
||||
l = string.len( num ) ;
|
||||
oldLen = bnum1.len ;
|
||||
if (l > RADIX_LEN) then
|
||||
local mod = l-( math.floor( l / RADIX_LEN ) * RADIX_LEN ) ;
|
||||
for i = 1 , l-mod, RADIX_LEN do
|
||||
bnum1[j] = tonumber( string.sub( num, -( i + RADIX_LEN - 1 ) , -i ) );
|
||||
--Check if string dosn't represents a number
|
||||
if bnum1[j] == nil then
|
||||
error( "Function BigNum.change: string is not a valid number" ) ;
|
||||
bnum1.len = 0 ;
|
||||
return 1 ;
|
||||
end
|
||||
j = j + 1 ;
|
||||
len = len + 1 ;
|
||||
end
|
||||
if (mod ~= 0) then
|
||||
bnum1[j] = tonumber( string.sub( num , 1 , mod ) ) ;
|
||||
bnum1.len = len + 1 ;
|
||||
else
|
||||
bnum1.len = len ;
|
||||
end
|
||||
--Eliminate trailing zeros
|
||||
for i = bnum1.len - 1 , 1 , -1 do
|
||||
if bnum1[i] == 0 then
|
||||
bnum1[i] = nil ;
|
||||
bnum1.len = bnum1.len - 1 ;
|
||||
else
|
||||
break ;
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- string.len(num) <= RADIX_LEN
|
||||
bnum1[j] = tonumber( num ) ;
|
||||
bnum1.len = 1 ;
|
||||
end
|
||||
else
|
||||
error( "Function BigNum.change: parameter error, type unexpected" ) ;
|
||||
end
|
||||
|
||||
--eliminates the deprecated higher order 'algarisms'
|
||||
if oldLen ~= nil then
|
||||
for i = bnum1.len , oldLen do
|
||||
bnum1[i] = nil ;
|
||||
end
|
||||
end
|
||||
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigNum.put( bnum , int , pos )
|
||||
if bnum == nil then
|
||||
error("Function BigNum.put: parameter nil") ;
|
||||
end
|
||||
local i = 0 ;
|
||||
for i = 0 , pos - 1 do
|
||||
bnum[i] = 0 ;
|
||||
end
|
||||
bnum[pos] = int ;
|
||||
for i = pos + 1 , bnum.len do
|
||||
bnum[i] = nil ;
|
||||
end
|
||||
bnum.len = pos ;
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
--printraw{{{2
|
||||
function printraw( bnum )
|
||||
local i = 0 ;
|
||||
if bnum == nil then
|
||||
error( "Function printraw: parameter nil" ) ;
|
||||
end
|
||||
while 1 == 1 do
|
||||
if bnum[i] == nil then
|
||||
io.write( ' len '..bnum.len ) ;
|
||||
if i ~= bnum.len then
|
||||
io.write( ' ERRO!!!!!!!!' ) ;
|
||||
end
|
||||
io.write( "\n" ) ;
|
||||
return 0 ;
|
||||
end
|
||||
io.write( 'r'..bnum[i] ) ;
|
||||
i = i + 1 ;
|
||||
end
|
||||
end
|
||||
--max{{{2
|
||||
function max( int1 , int2 )
|
||||
if int1 > int2 then
|
||||
return int1 ;
|
||||
else
|
||||
return int2 ;
|
||||
end
|
||||
end
|
||||
|
||||
--decr{{{2
|
||||
function decr( bnum1 )
|
||||
local temp = {} ;
|
||||
temp = BigNum.new( "1" ) ;
|
||||
BigNum.sub( bnum1 , temp , bnum1 ) ;
|
||||
return 0 ;
|
||||
end
|
||||
227
examples/love2d Threading Example/bin/numbers/BigRat.lua
Normal file
227
examples/love2d Threading Example/bin/numbers/BigRat.lua
Normal file
@ -0,0 +1,227 @@
|
||||
require( "bin.numbers.BigNum" ) ;
|
||||
|
||||
BigRat = {} ;
|
||||
BigRat.mt = {} ;
|
||||
function BigRat.new( num1 , num2 ) --{{{2
|
||||
local bigrat = {} ;
|
||||
local f ;
|
||||
setmetatable(bigrat, BigRat.mt) ;
|
||||
if type( num1 ) == "table" then
|
||||
if num1.num ~= nil and num1.den ~= nil then
|
||||
bigrat.num = BigNum.new( num1.num ) ;
|
||||
bigrat.den = BigNum.new( num1.den ) ;
|
||||
else
|
||||
bigrat.num = BigNum.new( num1 ) ;
|
||||
bigrat.den = BigNum.new( "1" ) ;
|
||||
end
|
||||
elseif num1 ~= nil then
|
||||
if num2 == nil then
|
||||
bigrat.den = BigNum.new( "1" ) ;
|
||||
else
|
||||
bigrat.den = BigNum.new( num2 ) ;
|
||||
end
|
||||
bigrat.num = BigNum.new( num1 ) ;
|
||||
else
|
||||
bigrat.den = BigNum.new( ) ;
|
||||
bigrat.num = BigNum.new( ) ;
|
||||
end
|
||||
|
||||
--Update the signals
|
||||
if bigrat.den.signal == "-" then
|
||||
if bigrat.num.signal == "-" then
|
||||
bigrat.num.signal = "+" ;
|
||||
else
|
||||
bigrat.num.signal = "-" ;
|
||||
end
|
||||
bigrat.den.signal = "+" ;
|
||||
end
|
||||
|
||||
return bigrat ;
|
||||
end
|
||||
|
||||
function BigRat.mt.sub( num1 , num2 )
|
||||
local temp = BigRat.new() ;
|
||||
local brat1 = BigRat.new( num1 ) ;
|
||||
local brat2 = BigRat.new( num2 ) ;
|
||||
BigRat.sub( brat1 , brat2 , temp ) ;
|
||||
return temp ;
|
||||
end
|
||||
|
||||
function BigRat.mt.add( num1 , num2 )
|
||||
local temp = BigRat.new() ;
|
||||
local brat1 = BigRat.new( num1 ) ;
|
||||
local brat2 = BigRat.new( num2 ) ;
|
||||
BigRat.add( brat1 , brat2 , temp ) ;
|
||||
return temp ;
|
||||
end
|
||||
|
||||
function BigRat.mt.mul( num1 , num2 )
|
||||
local temp = BigRat.new() ;
|
||||
local brat1 = BigRat.new( num1 ) ;
|
||||
local brat2 = BigRat.new( num2 ) ;
|
||||
BigRat.mul( brat1 , brat2 , temp ) ;
|
||||
return temp ;
|
||||
end
|
||||
|
||||
function BigRat.mt.div( num1 , num2 )
|
||||
local brat1 = BigRat.new( num1 ) ;
|
||||
local brat2 = BigRat.new( num2 ) ;
|
||||
local brat3 = BigRat.new() ;
|
||||
local brat4 = BigRat.new() ;
|
||||
BigRat.div( brat1 , brat2 , brat3 , brat4 ) ;
|
||||
return brat3 , brat4 ;
|
||||
end
|
||||
|
||||
function BigRat.mt.tostring( brat )
|
||||
BigRat.simplify( brat ) ;
|
||||
return BigNum.mt.tostring( brat.num ) .. " / " .. BigNum.mt.tostring( brat.den ) ;
|
||||
end
|
||||
|
||||
function BigRat.mt.pow ( num1 , num2 )
|
||||
local brat1 = BigRat.new( num1 ) ;
|
||||
local brat2 = BigRat.new( num2 ) ;
|
||||
return BigRat.pow( brat1 , brat2 )
|
||||
end
|
||||
|
||||
function BigRat.mt.eq ( num1 , num2 )
|
||||
return BigRat.eq( num1 , num2 )
|
||||
end
|
||||
|
||||
function BigRat.mt.lt ( num1 , num2 )
|
||||
return BigRat.lt( num1 , num2 )
|
||||
end
|
||||
|
||||
function BigRat.mt.le ( num1 , num2 )
|
||||
return BigRat.le( num1 , num2 )
|
||||
end
|
||||
|
||||
function BigRat.mt.unm ( num )
|
||||
local ret = BigRat.new( num )
|
||||
if ret.num.signal == '-' then
|
||||
ret.num.signal = '+'
|
||||
else
|
||||
ret.num.signal = '-'
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
BigRat.mt.__metatable = "hidden"
|
||||
BigRat.mt.__tostring = BigRat.mt.tostring
|
||||
BigRat.mt.__add = BigRat.mt.add
|
||||
BigRat.mt.__sub = BigRat.mt.sub
|
||||
BigRat.mt.__mul = BigRat.mt.mul
|
||||
BigRat.mt.__div = BigRat.mt.div
|
||||
BigRat.mt.__pow = BigRat.mt.pow
|
||||
BigRat.mt.__unm = BigRat.mt.unm
|
||||
BigRat.mt.__eq = BigRat.mt.eq
|
||||
BigRat.mt.__le = BigRat.mt.le
|
||||
BigRat.mt.__lt = BigRat.mt.lt
|
||||
setmetatable( BigRat.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ;
|
||||
function BigRat.add( brat1 , brat2 , brat3 )
|
||||
brat3.den = brat1.den * brat2.den ;
|
||||
brat3.num = ( brat1.num * brat2.den ) + ( brat2.num * brat1.den ) ;
|
||||
return brat3 ;
|
||||
end
|
||||
function BigRat.sub( brat1 , brat2 , brat3 )
|
||||
brat3.den = brat1.den * brat2.den ;
|
||||
brat3.num = ( brat1.num * brat2.den ) - ( brat2.num * brat1.den ) ;
|
||||
return brat3 ;
|
||||
end
|
||||
|
||||
function BigRat.mul( brat1 , brat2 , brat3 )
|
||||
brat3.num = brat1.num * brat2.num ;
|
||||
brat3.den = brat1.den * brat2.den ;
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigRat.div( brat1 , brat2 , brat3 )
|
||||
brat3.num = brat1.num * brat2.den ;
|
||||
brat3.den = brat1.den * brat2.num ;
|
||||
return brat3 ;
|
||||
end
|
||||
|
||||
function BigRat.pow( bnum1 , bnum2 )
|
||||
if bnum1 == nil or bnum2 == nil then
|
||||
error( "Function BigRat.pow: parameter nil" ) ;
|
||||
end
|
||||
local x = BigRat.new( "8" ) ;
|
||||
local n = BigRat.new( bnum2.den ) ;
|
||||
local n2 ;
|
||||
local y = BigRat.new( ) ;
|
||||
local i ;
|
||||
local temp = BigRat.new( ) ;
|
||||
|
||||
BigRat.simplify( bnum2 ) ;
|
||||
temp.num = BigNum.exp( bnum1.num , bnum2.num ) ;
|
||||
temp.den = BigNum.exp( bnum1.den , bnum2.num ) ;
|
||||
n2 = n - 1 ;
|
||||
|
||||
for i = 0 , 4 do
|
||||
y.num = x.num ^ n2.num ;
|
||||
y.den = x.den ^ n2.num ;
|
||||
x = (( temp / y ) + ( n2 * x )) / n ;
|
||||
end
|
||||
return x ;
|
||||
end
|
||||
|
||||
function BigRat.simplify( brat )
|
||||
if brat == nil then
|
||||
error( "Function BigRat.simplify: parameter nil" ) ;
|
||||
end
|
||||
local gcd = BigNum.new( ) ;
|
||||
local temp = BigRat.new( brat ) ;
|
||||
local devnull = BigNum.new( ) ;
|
||||
local zero = BigNum.new( "0" ) ;
|
||||
--Check if numerator is zero
|
||||
if BigNum.compareAbs( brat.num , zero ) == 0 then
|
||||
brat.den = BigNum.new( "1" ) ;
|
||||
return 0 ;
|
||||
end
|
||||
gcd = BigNum.gcd( brat.num , brat.den ) ;
|
||||
BigNum.div( temp.num , gcd , brat.num , devnull ) ;
|
||||
BigNum.div( temp.den , gcd , brat.den , devnull ) ;
|
||||
--Update the signal
|
||||
if brat.num.signal == '-' and brat.den.signal == '-' then
|
||||
brat.num.signal = '+' ;
|
||||
brat.den.signal = '+' ;
|
||||
end
|
||||
return 0 ;
|
||||
end
|
||||
|
||||
function BigRat.eq( brat1 , brat2 )
|
||||
if BigRat.compare( brat1 , brat2 ) == 0 then
|
||||
return true ;
|
||||
else
|
||||
return false ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigRat.lt( brat1 , brat2 )
|
||||
if BigRat.compare( brat1 , brat2 ) == 2 then
|
||||
return true ;
|
||||
else
|
||||
return false ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigRat.le( brat1 , brat2 )
|
||||
local temp = -1 ;
|
||||
temp = BigRat.compare( brat1 , brat2 )
|
||||
if temp == 0 or temp == 2 then
|
||||
return true ;
|
||||
else
|
||||
return false ;
|
||||
end
|
||||
end
|
||||
|
||||
function BigRat.compare( bnum1 , bnum2 )
|
||||
local temp ;
|
||||
temp = bnum1 - bnum2 ;
|
||||
if temp.num[0] == 0 and temp.num.len == 1 then --Check if is zero
|
||||
return 0 ;
|
||||
elseif temp.num.signal == "-" then
|
||||
return 2 ;
|
||||
else
|
||||
return 1 ;
|
||||
end
|
||||
end
|
||||
191
examples/love2d Threading Example/bin/numbers/bits.lua
Normal file
191
examples/love2d Threading Example/bin/numbers/bits.lua
Normal file
@ -0,0 +1,191 @@
|
||||
local bits={}
|
||||
bits.data=''
|
||||
bits.t='bits'
|
||||
bits.Type='bits'
|
||||
bits.__index = bits
|
||||
bits.__tostring=function(self) return self.data end
|
||||
bits.__len=function(self) return (#self.data)/8 end
|
||||
local floor,insert = math.floor, table.insert
|
||||
function bits.newBitBuffer(n)
|
||||
--
|
||||
end
|
||||
function bits.newConverter(bitsIn,bitsOut)
|
||||
local c={}
|
||||
--
|
||||
end
|
||||
function basen(n,b)
|
||||
if not b or b == 10 then return tostring(n) end
|
||||
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
local t = {}
|
||||
local sign = ""
|
||||
if n < 0 then
|
||||
sign = "-"
|
||||
n = -n
|
||||
end
|
||||
repeat
|
||||
local d = n % b + 1
|
||||
n = n / b
|
||||
insert(t, 1, digits:sub(d,d))
|
||||
until n == 0
|
||||
return sign .. table.concat(t,"")
|
||||
end
|
||||
bits.ref={}
|
||||
function bits.newByte(d)
|
||||
local c={}
|
||||
if type(d)=="string" then
|
||||
if #d>1 or #d<1 then
|
||||
error("A byte must be one character!")
|
||||
else
|
||||
c.data=string.byte(d)
|
||||
end
|
||||
elseif type(d)=="number" then
|
||||
if d>255 or d<0 then
|
||||
error("A byte must be between 0 and 255!")
|
||||
else
|
||||
c.data=d
|
||||
end
|
||||
else
|
||||
error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!")
|
||||
end
|
||||
c.__index=function(self,k)
|
||||
if k>=0 and k<9 then
|
||||
if self.data==0 then
|
||||
return 0
|
||||
elseif self.data==255 then
|
||||
return 1
|
||||
else
|
||||
return bits.ref[self.data][k]
|
||||
end
|
||||
end
|
||||
end
|
||||
c.__tostring=function(self)
|
||||
return bits.ref[tostring(self.data)]
|
||||
end
|
||||
setmetatable(c,c)
|
||||
return c
|
||||
end
|
||||
function bits.newByteArray(s)
|
||||
local c={}
|
||||
if type(s)~="string" then
|
||||
error("Must be a string type or bin/buffer type")
|
||||
elseif type(s)=="table" then
|
||||
if s.t=="sink" or s.t=="buffer" or s.t=="bin" then
|
||||
local data=s:getData()
|
||||
for i=1,#data do
|
||||
c[#c+1]=bits.newByte(data:sub(i,i))
|
||||
end
|
||||
else
|
||||
error("Must be a string type or bin/buffer type")
|
||||
end
|
||||
else
|
||||
for i=1,#s do
|
||||
c[#c+1]=bits.newByte(s:sub(i,i))
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
function bits.new(n,binary)
|
||||
local temp={}
|
||||
temp.t="bits"
|
||||
temp.Type="bits"
|
||||
if type(n)=="string" then
|
||||
if binary then
|
||||
temp.data=n:match("[10]+")
|
||||
else
|
||||
local t={}
|
||||
for i=#n,1,-1 do
|
||||
table.insert(t,bits:conv(string.byte(n,i)))
|
||||
end
|
||||
temp.data=table.concat(t)
|
||||
end
|
||||
elseif type(n)=="number" or type(n)=="table" then
|
||||
temp.data=basen(n,2)
|
||||
end
|
||||
if #temp.data%8~=0 then
|
||||
temp.data=string.rep('0',8-#temp.data%8)..temp.data
|
||||
end
|
||||
setmetatable(temp, bits)
|
||||
return temp
|
||||
end
|
||||
for i=0,255 do
|
||||
local d=bits.new(i).data
|
||||
bits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")}
|
||||
bits.ref[tostring(i)]=d
|
||||
bits.ref[d]=i
|
||||
bits.ref["\255"..string.char(i)]=d
|
||||
end
|
||||
function bits.numToBytes(n,fit,func)
|
||||
local num=string.reverse(bits.new(n):toSbytes())
|
||||
local ref={["num"]=num,["fit"]=fit}
|
||||
if fit then
|
||||
if fit<#num then
|
||||
if func then
|
||||
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
|
||||
func(ref)
|
||||
else
|
||||
print("Warning: attempting to store a number that takes up more space than allotted!")
|
||||
end
|
||||
return ref.num:sub(1,ref.fit)
|
||||
elseif fit==#num then
|
||||
return string.reverse(num)
|
||||
else
|
||||
return string.reverse(string.rep("\0",fit-#num)..num)
|
||||
end
|
||||
else
|
||||
return string.reverse(num)
|
||||
end
|
||||
end
|
||||
function bits:conv(n)
|
||||
local tab={}
|
||||
while n>=1 do
|
||||
table.insert(tab,n%2)
|
||||
n=math.floor(n/2)
|
||||
end
|
||||
local str=string.reverse(table.concat(tab))
|
||||
if #str%8~=0 or #str==0 then
|
||||
str=string.rep('0',8-#str%8)..str
|
||||
end
|
||||
return str
|
||||
end
|
||||
function bits:tonumber(s,e)
|
||||
if s==0 then
|
||||
return tonumber(self.data,2)
|
||||
end
|
||||
s=s or 1
|
||||
return tonumber(string.sub(self.data,(8*(s-1))+1,8*s),2) or error('Bounds!')
|
||||
end
|
||||
function bits:isover()
|
||||
return #self.data>8
|
||||
end
|
||||
function bits:flipbits()
|
||||
tab={}
|
||||
for i=1,#self.data do
|
||||
if string.sub(self.data,i,i)=='1' then
|
||||
table.insert(tab,'0')
|
||||
else
|
||||
table.insert(tab,'1')
|
||||
end
|
||||
end
|
||||
self.data=table.concat(tab)
|
||||
end
|
||||
function bits:tobytes()
|
||||
local tab={}
|
||||
for i=self:getbytes(),1,-1 do
|
||||
table.insert(tab,string.char(self:tonumber(i)))
|
||||
end
|
||||
return bin.new(table.concat(tab))
|
||||
end
|
||||
function bits:toSbytes()
|
||||
local tab={}
|
||||
for i=self:getbytes(),1,-1 do
|
||||
table.insert(tab,string.char(self:tonumber(i)))
|
||||
end
|
||||
return table.concat(tab)
|
||||
end
|
||||
function bits:getBin()
|
||||
return self.data
|
||||
end
|
||||
function bits:getbytes()
|
||||
return #self.data/8
|
||||
end
|
||||
return bits
|
||||
244
examples/love2d Threading Example/bin/numbers/infinabits.lua
Normal file
244
examples/love2d Threading Example/bin/numbers/infinabits.lua
Normal file
@ -0,0 +1,244 @@
|
||||
local binNum=require("bin.numbers.BigNum")
|
||||
local infinabits={}
|
||||
infinabits.data=''
|
||||
infinabits.t='infinabits'
|
||||
infinabits.Type='infinabits'
|
||||
infinabits.__index = infinabits
|
||||
infinabits.__tostring=function(self) return self.data end
|
||||
infinabits.__len=function(self) return (#self.data)/8 end
|
||||
local floor,insert = math.floor, table.insert
|
||||
function basen(n,b)
|
||||
n=BigNum.new(n)
|
||||
if not b or b == 10 then return tostring(n) end
|
||||
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
local t = {}
|
||||
local sign = ""
|
||||
if n < BigNum.new(0) then
|
||||
sign = "-"
|
||||
n = -n
|
||||
end
|
||||
repeat
|
||||
local d = tonumber(tostring(n % b)) + 1
|
||||
n = n / b
|
||||
insert(t, 1, digits:sub(d,d))
|
||||
until n == BigNum.new(0)
|
||||
return sign .. table.concat(t,"")
|
||||
end
|
||||
function base2to10(num)
|
||||
local n=BigNum.new(0)
|
||||
for i = #num-1,0,-1 do
|
||||
nn=BigNum.new(num:sub(i+1,i+1))*(BigNum.new(2)^((#num-i)-1))
|
||||
n=n+nn
|
||||
end
|
||||
return n
|
||||
end
|
||||
function infinabits.newBitBuffer(n)
|
||||
-- WIP
|
||||
end
|
||||
function infinabits.newConverter(bitsIn,bitsOut)
|
||||
local c={}
|
||||
-- WIP
|
||||
end
|
||||
infinabits.ref={}
|
||||
function infinabits.newByte(d)-- WIP
|
||||
local c={}
|
||||
if type(d)=="string" then
|
||||
if #d>1 or #d<1 then
|
||||
error("A byte must be one character!")
|
||||
else
|
||||
c.data=string.byte(d)
|
||||
end
|
||||
elseif type(d)=="number" then
|
||||
if d>255 or d<0 then
|
||||
error("A byte must be between 0 and 255!")
|
||||
else
|
||||
c.data=d
|
||||
end
|
||||
else
|
||||
error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!")
|
||||
end
|
||||
c.__index=function(self,k)
|
||||
if k>=0 and k<9 then
|
||||
if self.data==0 then
|
||||
return 0
|
||||
elseif self.data==255 then
|
||||
return 1
|
||||
else
|
||||
return infinabits.ref[self.data][k]
|
||||
end
|
||||
end
|
||||
end
|
||||
c.__tostring=function(self)
|
||||
return infinabits.ref[tostring(self.data)]
|
||||
end
|
||||
setmetatable(c,c)
|
||||
return c
|
||||
end
|
||||
function infinabits.newByteArray(s)-- WIP
|
||||
local c={}
|
||||
if type(s)~="string" then
|
||||
error("Must be a string type or bin/buffer type")
|
||||
elseif type(s)=="table" then
|
||||
if s.t=="sink" or s.t=="buffer" or s.t=="bin" then
|
||||
local data=s:getData()
|
||||
for i=1,#data do
|
||||
c[#c+1]=infinabits.newByte(data:sub(i,i))
|
||||
end
|
||||
else
|
||||
error("Must be a string type or bin/buffer type")
|
||||
end
|
||||
else
|
||||
for i=1,#s do
|
||||
c[#c+1]=infinabits.newByte(s:sub(i,i))
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
function infinabits.new(n,binary)
|
||||
local temp={}
|
||||
temp.t="infinabits"
|
||||
temp.Type="infinabits"
|
||||
if type(n)=="string" then
|
||||
if binary then
|
||||
temp.data=n:match("[10]+")
|
||||
else
|
||||
local t={}
|
||||
for i=#n,1,-1 do
|
||||
table.insert(t,infinabits:conv(string.byte(n,i)))
|
||||
end
|
||||
temp.data=table.concat(t)
|
||||
end
|
||||
elseif type(n)=="number" or type(n)=="table" then
|
||||
temp.data=basen(tostring(n),2)
|
||||
end
|
||||
if #temp.data%8~=0 then
|
||||
temp.data=string.rep('0',8-#temp.data%8)..temp.data
|
||||
end
|
||||
setmetatable(temp, infinabits)
|
||||
return temp
|
||||
end
|
||||
for i=0,255 do
|
||||
local d=infinabits.new(i).data
|
||||
infinabits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")}
|
||||
infinabits.ref[tostring(i)]=d
|
||||
infinabits.ref[d]=i
|
||||
infinabits.ref["\255"..string.char(i)]=d
|
||||
end
|
||||
function infinabits.numToBytes(n,fit,func)
|
||||
local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
|
||||
local ref={["num"]=num,["fit"]=fit}
|
||||
if fit then
|
||||
if fit<#num then
|
||||
if func then
|
||||
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
|
||||
func(ref)
|
||||
else
|
||||
print("Warning: attempting to store a number that takes up more space than allotted!")
|
||||
end
|
||||
return ref.num:sub(1,ref.fit)
|
||||
elseif fit==#num then
|
||||
return string.reverse(num)
|
||||
else
|
||||
return string.reverse(string.rep("\0",fit-#num)..num)
|
||||
end
|
||||
else
|
||||
return string.reverse(num)
|
||||
end
|
||||
end
|
||||
function infinabits.numToBytes(n,fit,fmt,func)
|
||||
if fmt=="%e" then
|
||||
local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
|
||||
local ref={["num"]=num,["fit"]=fit}
|
||||
if fit then
|
||||
if fit<#num then
|
||||
if func then
|
||||
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
|
||||
func(ref)
|
||||
else
|
||||
print("Warning: attempting to store a number that takes up more space than allotted!")
|
||||
end
|
||||
return ref.num:sub(1,ref.fit)
|
||||
elseif fit==#num then
|
||||
return num
|
||||
else
|
||||
return string.rep("\0",fit-#num)..num
|
||||
end
|
||||
else
|
||||
return num
|
||||
end
|
||||
|
||||
else
|
||||
local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes())
|
||||
local ref={["num"]=num,["fit"]=fit}
|
||||
if fit then
|
||||
if fit<#num then
|
||||
if func then
|
||||
print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!")
|
||||
func(ref)
|
||||
else
|
||||
print("Warning: attempting to store a number that takes up more space than allotted!")
|
||||
end
|
||||
return ref.num:sub(1,ref.fit)
|
||||
elseif fit==#num then
|
||||
return string.reverse(num)
|
||||
else
|
||||
return string.reverse(string.rep("\0",fit-#num)..num)
|
||||
end
|
||||
else
|
||||
return string.reverse(num)
|
||||
end
|
||||
end
|
||||
end
|
||||
function infinabits:conv(n)
|
||||
local tab={}
|
||||
local one=BigNum.new(1)
|
||||
local n=BigNum.new(n)
|
||||
while n>=one do
|
||||
table.insert(tab,tonumber(tostring(n%2)))
|
||||
n=n/2
|
||||
end
|
||||
local str=string.reverse(table.concat(tab))
|
||||
if #str%8~=0 or #str==0 then
|
||||
str=string.rep('0',8-#str%8)..str
|
||||
end
|
||||
return str
|
||||
end
|
||||
function infinabits:tonumber(s)
|
||||
if s==0 then
|
||||
return tonumber(self.data,2)
|
||||
end
|
||||
s=s or 1
|
||||
return tonumber(tostring(base2to10(string.sub(self.data,(8*(s-1))+1,8*s)))) or error('Bounds!')
|
||||
end
|
||||
function infinabits:isover()
|
||||
return #self.data>8
|
||||
end
|
||||
function infinabits:flipbits()
|
||||
tab={}
|
||||
local s=self.data
|
||||
s=s:gsub("1","_")
|
||||
s=s:gsub("0","1")
|
||||
s=s:gsub("_","0")
|
||||
self.data=s
|
||||
end
|
||||
function infinabits:tobytes()
|
||||
local tab={}
|
||||
for i=self:getbytes(),1,-1 do
|
||||
table.insert(tab,string.char(self:tonumber(i)))
|
||||
end
|
||||
return bin.new(table.concat(tab))
|
||||
end
|
||||
function infinabits:toSbytes()
|
||||
local tab={}
|
||||
for i=self:getbytes(),1,-1 do
|
||||
table.insert(tab,string.char(self:tonumber(i)))
|
||||
end
|
||||
return table.concat(tab)
|
||||
end
|
||||
function infinabits:getBin()
|
||||
return self.data
|
||||
end
|
||||
function infinabits:getbytes()
|
||||
return #self.data/8
|
||||
end
|
||||
return infinabits
|
||||
333
examples/love2d Threading Example/bin/numbers/no_jit_bit.lua
Normal file
333
examples/love2d Threading Example/bin/numbers/no_jit_bit.lua
Normal file
@ -0,0 +1,333 @@
|
||||
--[[
|
||||
LICENSE
|
||||
|
||||
(c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
|
||||
--]]
|
||||
|
||||
local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'}
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
local MOD = 2^32
|
||||
local MODM = MOD-1
|
||||
|
||||
local function memoize(f)
|
||||
local mt = {}
|
||||
local t = setmetatable({}, mt)
|
||||
function mt:__index(k)
|
||||
local v = f(k); t[k] = v
|
||||
return v
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function make_bitop_uncached(t, m)
|
||||
local function bitop(a, b)
|
||||
local res,p = 0,1
|
||||
while a ~= 0 and b ~= 0 do
|
||||
local am, bm = a%m, b%m
|
||||
res = res + t[am][bm]*p
|
||||
a = (a - am) / m
|
||||
b = (b - bm) / m
|
||||
p = p*m
|
||||
end
|
||||
res = res + (a+b)*p
|
||||
return res
|
||||
end
|
||||
return bitop
|
||||
end
|
||||
|
||||
local function make_bitop(t)
|
||||
local op1 = make_bitop_uncached(t,2^1)
|
||||
local op2 = memoize(function(a)
|
||||
return memoize(function(b)
|
||||
return op1(a, b)
|
||||
end)
|
||||
end)
|
||||
return make_bitop_uncached(op2, 2^(t.n or 1))
|
||||
end
|
||||
|
||||
-- ok? probably not if running on a 32-bit int Lua number type platform
|
||||
function M.tobit(x)
|
||||
return x % 2^32
|
||||
end
|
||||
|
||||
M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}
|
||||
local bxor = M.bxor
|
||||
|
||||
function M.bnot(a) return MODM - a end
|
||||
local bnot = M.bnot
|
||||
|
||||
function M.band(a,b) return ((a+b) - bxor(a,b))/2 end
|
||||
local band = M.band
|
||||
|
||||
function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end
|
||||
local bor = M.bor
|
||||
|
||||
local lshift, rshift -- forward declare
|
||||
|
||||
function M.rshift(a,disp) -- Lua5.2 insipred
|
||||
if disp < 0 then return lshift(a,-disp) end
|
||||
return floor(a % 2^32 / 2^disp)
|
||||
end
|
||||
rshift = M.rshift
|
||||
|
||||
function M.lshift(a,disp) -- Lua5.2 inspired
|
||||
if disp < 0 then return rshift(a,-disp) end
|
||||
return (a * 2^disp) % 2^32
|
||||
end
|
||||
lshift = M.lshift
|
||||
|
||||
function M.tohex(x, n) -- BitOp style
|
||||
n = n or 8
|
||||
local up
|
||||
if n <= 0 then
|
||||
if n == 0 then return '' end
|
||||
up = true
|
||||
n = - n
|
||||
end
|
||||
x = band(x, 16^n-1)
|
||||
return ('%0'..n..(up and 'X' or 'x')):format(x)
|
||||
end
|
||||
local tohex = M.tohex
|
||||
|
||||
function M.extract(n, field, width) -- Lua5.2 inspired
|
||||
width = width or 1
|
||||
return band(rshift(n, field), 2^width-1)
|
||||
end
|
||||
local extract = M.extract
|
||||
|
||||
function M.replace(n, v, field, width) -- Lua5.2 inspired
|
||||
width = width or 1
|
||||
local mask1 = 2^width-1
|
||||
v = band(v, mask1) -- required by spec?
|
||||
local mask = bnot(lshift(mask1, field))
|
||||
return band(n, mask) + lshift(v, field)
|
||||
end
|
||||
local replace = M.replace
|
||||
|
||||
function M.bswap(x) -- BitOp style
|
||||
local a = band(x, 0xff); x = rshift(x, 8)
|
||||
local b = band(x, 0xff); x = rshift(x, 8)
|
||||
local c = band(x, 0xff); x = rshift(x, 8)
|
||||
local d = band(x, 0xff)
|
||||
return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d
|
||||
end
|
||||
local bswap = M.bswap
|
||||
|
||||
function M.rrotate(x, disp) -- Lua5.2 inspired
|
||||
disp = disp % 32
|
||||
local low = band(x, 2^disp-1)
|
||||
return rshift(x, disp) + lshift(low, 32-disp)
|
||||
end
|
||||
local rrotate = M.rrotate
|
||||
|
||||
function M.lrotate(x, disp) -- Lua5.2 inspired
|
||||
return rrotate(x, -disp)
|
||||
end
|
||||
local lrotate = M.lrotate
|
||||
|
||||
M.rol = M.lrotate -- LuaOp inspired
|
||||
M.ror = M.rrotate -- LuaOp insipred
|
||||
|
||||
|
||||
function M.arshift(x, disp) -- Lua5.2 inspired
|
||||
local z = rshift(x, disp)
|
||||
if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
|
||||
return z
|
||||
end
|
||||
local arshift = M.arshift
|
||||
|
||||
function M.btest(x, y) -- Lua5.2 inspired
|
||||
return band(x, y) ~= 0
|
||||
end
|
||||
|
||||
M.bit32 = {} -- Lua 5.2 'bit32' compatibility
|
||||
|
||||
|
||||
local function bit32_bnot(x)
|
||||
return (-1 - x) % MOD
|
||||
end
|
||||
M.bit32.bnot = bit32_bnot
|
||||
|
||||
local function bit32_bxor(a, b, c, ...)
|
||||
local z
|
||||
if b then
|
||||
a = a % MOD
|
||||
b = b % MOD
|
||||
z = bxor(a, b)
|
||||
if c then
|
||||
z = bit32_bxor(z, c, ...)
|
||||
end
|
||||
return z
|
||||
elseif a then
|
||||
return a % MOD
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
M.bit32.bxor = bit32_bxor
|
||||
|
||||
local function bit32_band(a, b, c, ...)
|
||||
local z
|
||||
if b then
|
||||
a = a % MOD
|
||||
b = b % MOD
|
||||
z = ((a+b) - bxor(a,b)) / 2
|
||||
if c then
|
||||
z = bit32_band(z, c, ...)
|
||||
end
|
||||
return z
|
||||
elseif a then
|
||||
return a % MOD
|
||||
else
|
||||
return MODM
|
||||
end
|
||||
end
|
||||
M.bit32.band = bit32_band
|
||||
|
||||
local function bit32_bor(a, b, c, ...)
|
||||
local z
|
||||
if b then
|
||||
a = a % MOD
|
||||
b = b % MOD
|
||||
z = MODM - band(MODM - a, MODM - b)
|
||||
if c then
|
||||
z = bit32_bor(z, c, ...)
|
||||
end
|
||||
return z
|
||||
elseif a then
|
||||
return a % MOD
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
M.bit32.bor = bit32_bor
|
||||
|
||||
function M.bit32.btest(...)
|
||||
return bit32_band(...) ~= 0
|
||||
end
|
||||
|
||||
function M.bit32.lrotate(x, disp)
|
||||
return lrotate(x % MOD, disp)
|
||||
end
|
||||
|
||||
function M.bit32.rrotate(x, disp)
|
||||
return rrotate(x % MOD, disp)
|
||||
end
|
||||
|
||||
function M.bit32.lshift(x,disp)
|
||||
if disp > 31 or disp < -31 then return 0 end
|
||||
return lshift(x % MOD, disp)
|
||||
end
|
||||
|
||||
function M.bit32.rshift(x,disp)
|
||||
if disp > 31 or disp < -31 then return 0 end
|
||||
return rshift(x % MOD, disp)
|
||||
end
|
||||
|
||||
function M.bit32.arshift(x,disp)
|
||||
x = x % MOD
|
||||
if disp >= 0 then
|
||||
if disp > 31 then
|
||||
return (x >= 0x80000000) and MODM or 0
|
||||
else
|
||||
local z = rshift(x, disp)
|
||||
if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end
|
||||
return z
|
||||
end
|
||||
else
|
||||
return lshift(x, -disp)
|
||||
end
|
||||
end
|
||||
|
||||
function M.bit32.extract(x, field, ...)
|
||||
local width = ... or 1
|
||||
if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
|
||||
x = x % MOD
|
||||
return extract(x, field, ...)
|
||||
end
|
||||
|
||||
function M.bit32.replace(x, v, field, ...)
|
||||
local width = ... or 1
|
||||
if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end
|
||||
x = x % MOD
|
||||
v = v % MOD
|
||||
return replace(x, v, field, ...)
|
||||
end
|
||||
|
||||
M.bit = {} -- LuaBitOp "bit" compatibility
|
||||
|
||||
function M.bit.tobit(x)
|
||||
x = x % MOD
|
||||
if x >= 0x80000000 then x = x - MOD end
|
||||
return x
|
||||
end
|
||||
local bit_tobit = M.bit.tobit
|
||||
|
||||
function M.bit.tohex(x, ...)
|
||||
return tohex(x % MOD, ...)
|
||||
end
|
||||
|
||||
function M.bit.bnot(x)
|
||||
return bit_tobit(bnot(x % MOD))
|
||||
end
|
||||
|
||||
local function bit_bor(a, b, c, ...)
|
||||
if c then
|
||||
return bit_bor(bit_bor(a, b), c, ...)
|
||||
elseif b then
|
||||
return bit_tobit(bor(a % MOD, b % MOD))
|
||||
else
|
||||
return bit_tobit(a)
|
||||
end
|
||||
end
|
||||
M.bit.bor = bit_bor
|
||||
|
||||
local function bit_band(a, b, c, ...)
|
||||
if c then
|
||||
return bit_band(bit_band(a, b), c, ...)
|
||||
elseif b then
|
||||
return bit_tobit(band(a % MOD, b % MOD))
|
||||
else
|
||||
return bit_tobit(a)
|
||||
end
|
||||
end
|
||||
M.bit.band = bit_band
|
||||
|
||||
local function bit_bxor(a, b, c, ...)
|
||||
if c then
|
||||
return bit_bxor(bit_bxor(a, b), c, ...)
|
||||
elseif b then
|
||||
return bit_tobit(bxor(a % MOD, b % MOD))
|
||||
else
|
||||
return bit_tobit(a)
|
||||
end
|
||||
end
|
||||
M.bit.bxor = bit_bxor
|
||||
|
||||
function M.bit.lshift(x, n)
|
||||
return bit_tobit(lshift(x % MOD, n % 32))
|
||||
end
|
||||
|
||||
function M.bit.rshift(x, n)
|
||||
return bit_tobit(rshift(x % MOD, n % 32))
|
||||
end
|
||||
|
||||
function M.bit.arshift(x, n)
|
||||
return bit_tobit(arshift(x % MOD, n % 32))
|
||||
end
|
||||
|
||||
function M.bit.rol(x, n)
|
||||
return bit_tobit(lrotate(x % MOD, n % 32))
|
||||
end
|
||||
|
||||
function M.bit.ror(x, n)
|
||||
return bit_tobit(rrotate(x % MOD, n % 32))
|
||||
end
|
||||
|
||||
function M.bit.bswap(x)
|
||||
return bit_tobit(bswap(x % MOD))
|
||||
end
|
||||
|
||||
return M
|
||||
232
examples/love2d Threading Example/bin/numbers/random.lua
Normal file
232
examples/love2d Threading Example/bin/numbers/random.lua
Normal file
@ -0,0 +1,232 @@
|
||||
--[[----------------------------------------
|
||||
Random
|
||||
Not all of this is mine
|
||||
------------------------------------------]]
|
||||
--[[------------------------------------
|
||||
RandomLua v0.3.1
|
||||
Pure Lua Pseudo-Random Numbers Generator
|
||||
Under the MIT license.
|
||||
copyright(c) 2011 linux-man
|
||||
--]]------------------------------------
|
||||
|
||||
local math_floor = math.floor
|
||||
|
||||
local function normalize(n)
|
||||
return n % 0x80000000
|
||||
end
|
||||
|
||||
local function bit_and(a, b)
|
||||
local r = 0
|
||||
local m = 0
|
||||
for m = 0, 31 do
|
||||
if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end
|
||||
if a % 2 ~= 0 then a = a - 1 end
|
||||
if b % 2 ~= 0 then b = b - 1 end
|
||||
a = a / 2 b = b / 2
|
||||
end
|
||||
return normalize(r)
|
||||
end
|
||||
|
||||
local function bit_or(a, b)
|
||||
local r = 0
|
||||
local m = 0
|
||||
for m = 0, 31 do
|
||||
if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end
|
||||
if a % 2 ~= 0 then a = a - 1 end
|
||||
if b % 2 ~= 0 then b = b - 1 end
|
||||
a = a / 2 b = b / 2
|
||||
end
|
||||
return normalize(r)
|
||||
end
|
||||
|
||||
local function bit_xor(a, b)
|
||||
local r = 0
|
||||
local m = 0
|
||||
for m = 0, 31 do
|
||||
if a % 2 ~= b % 2 then r = r + 2^m end
|
||||
if a % 2 ~= 0 then a = a - 1 end
|
||||
if b % 2 ~= 0 then b = b - 1 end
|
||||
a = a / 2 b = b / 2
|
||||
end
|
||||
return normalize(r)
|
||||
end
|
||||
|
||||
local function seed()
|
||||
return normalize(os.time())
|
||||
end
|
||||
|
||||
--Mersenne twister
|
||||
local mersenne_twister = {}
|
||||
mersenne_twister.__index = mersenne_twister
|
||||
|
||||
function mersenne_twister:randomseed(s)
|
||||
if not s then s = seed() end
|
||||
self.mt[0] = normalize(s)
|
||||
for i = 1, 623 do
|
||||
self.mt[i] = normalize(0x6c078965 * bit_xor(self.mt[i-1], math_floor(self.mt[i-1] / 0x40000000)) + i)
|
||||
end
|
||||
end
|
||||
|
||||
function mersenne_twister:random(a, b)
|
||||
local y
|
||||
if self.index == 0 then
|
||||
for i = 0, 623 do
|
||||
y = self.mt[(i + 1) % 624] % 0x80000000
|
||||
self.mt[i] = bit_xor(self.mt[(i + 397) % 624], math_floor(y / 2))
|
||||
if y % 2 ~= 0 then self.mt[i] = bit_xor(self.mt[i], 0x9908b0df) end
|
||||
end
|
||||
end
|
||||
y = self.mt[self.index]
|
||||
y = bit_xor(y, math_floor(y / 0x800))
|
||||
y = bit_xor(y, bit_and(normalize(y * 0x80), 0x9d2c5680))
|
||||
y = bit_xor(y, bit_and(normalize(y * 0x8000), 0xefc60000))
|
||||
y = bit_xor(y, math_floor(y / 0x40000))
|
||||
self.index = (self.index + 1) % 624
|
||||
if not a then return y / 0x80000000
|
||||
elseif not b then
|
||||
if a == 0 then return y
|
||||
else return 1 + (y % a)
|
||||
end
|
||||
else
|
||||
return a + (y % (b - a + 1))
|
||||
end
|
||||
end
|
||||
|
||||
local function twister(s)
|
||||
local temp = {}
|
||||
setmetatable(temp, mersenne_twister)
|
||||
temp.mt = {}
|
||||
temp.index = 0
|
||||
temp:randomseed(s)
|
||||
return temp
|
||||
end
|
||||
|
||||
--Linear Congruential Generator
|
||||
local linear_congruential_generator = {}
|
||||
linear_congruential_generator.__index = linear_congruential_generator
|
||||
|
||||
function linear_congruential_generator:random(a, b)
|
||||
local y = (self.a * self.x + self.c) % self.m
|
||||
self.x = y
|
||||
if not a then return y / 0x10000
|
||||
elseif not b then
|
||||
if a == 0 then return y
|
||||
else return 1 + (y % a) end
|
||||
else
|
||||
return a + (y % (b - a + 1))
|
||||
end
|
||||
end
|
||||
|
||||
function linear_congruential_generator:randomseed(s)
|
||||
if not s then s = seed() end
|
||||
self.x = normalize(s)
|
||||
end
|
||||
|
||||
local function lcg(s, r)
|
||||
local temp = {}
|
||||
setmetatable(temp, linear_congruential_generator)
|
||||
temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C
|
||||
if r then
|
||||
if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes.
|
||||
elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC
|
||||
end
|
||||
temp:randomseed(s)
|
||||
return temp
|
||||
end
|
||||
|
||||
-- Multiply-with-carry
|
||||
local multiply_with_carry = {}
|
||||
multiply_with_carry.__index = multiply_with_carry
|
||||
|
||||
function multiply_with_carry:random(a, b)
|
||||
local m = self.m
|
||||
local t = self.a * self.x + self.c
|
||||
local y = t % m
|
||||
self.x = y
|
||||
self.c = math_floor(t / m)
|
||||
if not a then return y / 0x10000
|
||||
elseif not b then
|
||||
if a == 0 then return y
|
||||
else return 1 + (y % a) end
|
||||
else
|
||||
return a + (y % (b - a + 1))
|
||||
end
|
||||
end
|
||||
|
||||
function multiply_with_carry:randomseed(s)
|
||||
if not s then s = seed() end
|
||||
self.c = self.ic
|
||||
self.x = normalize(s)
|
||||
end
|
||||
|
||||
local function mwc(s, r)
|
||||
local temp = {}
|
||||
setmetatable(temp, multiply_with_carry)
|
||||
temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C
|
||||
if r then
|
||||
if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes.
|
||||
elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC
|
||||
end
|
||||
temp.ic = temp.c
|
||||
temp:randomseed(s)
|
||||
return temp
|
||||
end
|
||||
-- Little bind for the methods: My code starts
|
||||
local randomGen={}
|
||||
randomGen.__index=randomGen
|
||||
function randomGen:new(s)
|
||||
local temp={}
|
||||
setmetatable(temp,randomGen)
|
||||
temp[1]=twister()
|
||||
temp[2]=lcg()
|
||||
temp[3]=mwc()
|
||||
temp.pos=1
|
||||
for i=1,3 do
|
||||
temp[i]:randomseed(s)
|
||||
end
|
||||
return temp
|
||||
end
|
||||
function randomGen:randomseed(s)
|
||||
self.pos=1
|
||||
self[1]:randomseed(s)
|
||||
self[2]:randomseed(s)
|
||||
self[3]:randomseed(s)
|
||||
end
|
||||
function randomGen:randomInt(a,b)
|
||||
local t=self[self.pos]:random(a,b)
|
||||
self.pos=self.pos+1
|
||||
if self.pos>3 then
|
||||
self.pos=1
|
||||
end
|
||||
return t
|
||||
end
|
||||
function randomGen:newND(a,b,s)
|
||||
if not(a) or not(b) then error('You must include a range!') end
|
||||
local temp=randomGen:new(s)
|
||||
temp.a=a
|
||||
temp.b=b
|
||||
temp.range=b-a+1
|
||||
temp.dups={no=0}
|
||||
function temp:nextInt()
|
||||
local t=self:randomInt(self.a,self.b)
|
||||
if self.dups[t]==nil then
|
||||
self.dups[t]=true
|
||||
self.dups.no=self.dups.no+1
|
||||
else
|
||||
return self:nextInt()
|
||||
end
|
||||
if self.dups.no==self.range then
|
||||
function self:nextInt()
|
||||
return 1,true
|
||||
end
|
||||
return t
|
||||
else
|
||||
return t
|
||||
end
|
||||
end
|
||||
function temp:nextIInt()
|
||||
return function() return self:nextInt() end
|
||||
end
|
||||
return temp
|
||||
end
|
||||
return randomGen
|
||||
146
examples/love2d Threading Example/bin/support/extraBlocks.lua
Normal file
146
examples/love2d Threading Example/bin/support/extraBlocks.lua
Normal file
@ -0,0 +1,146 @@
|
||||
local __CURRENTVERSION=2
|
||||
bin.registerBlock("t",function(SIZE_OR_NIL,ref)
|
||||
local header=ref:read(3)
|
||||
if not header:match("(LT.)") then error("Not a valid table struct!") end
|
||||
if bin.defualtBit.new(header:sub(3,3)):tonumber(1)>__CURRENTVERSION then error("Incompatible Version of LuaTable!") end
|
||||
local len=ref:getBlock("n",4) -- hehe lets make life easier
|
||||
local tab={}
|
||||
local ind
|
||||
local n=0
|
||||
while true do
|
||||
local _dat=ref:read(2)
|
||||
if _dat==nil then break end
|
||||
local it,dt=_dat:match("(.)(.)")
|
||||
n=n+2
|
||||
if it=="N" then -- get the index stuff out of the way first
|
||||
ind=ref:getBlock("n",4)
|
||||
n=n+4
|
||||
else
|
||||
indL=ref:getBlock("n",1)
|
||||
n=n+1+indL
|
||||
ind=ref:read(indL)
|
||||
end
|
||||
if dt=="N" then
|
||||
tab[ind]=ref:getBlock("d")
|
||||
n=n+8
|
||||
elseif dt=="I" then
|
||||
tab[ind]=math.huge
|
||||
ref:getBlock("n",4)
|
||||
n=n+4
|
||||
elseif dt=="i" then
|
||||
tab[ind]=-math.huge
|
||||
ref:getBlock("n",4)
|
||||
n=n+4
|
||||
elseif dt=="S" then
|
||||
local nn=ref:getBlock("n",4)
|
||||
tab[ind]=ref:read(nn)
|
||||
n=n+4+nn
|
||||
elseif dt=="B" then
|
||||
tab[ind]=({["\255"]=true,["\0"]=false})[ref:read(1)]
|
||||
n=n+1
|
||||
elseif dt=="F" then
|
||||
local nn=ref:getBlock("n",4)
|
||||
tab[ind]=loadstring(ref:read(nn))
|
||||
n=n+4+nn
|
||||
elseif dt=="T" then
|
||||
local cur=ref:getSeek()
|
||||
local size=ref:getBlock("n",4)
|
||||
ref:setSeek(cur)
|
||||
ref:read(4)
|
||||
if size==7 then
|
||||
tab[ind]={}
|
||||
ref:read(7)
|
||||
n=n+11
|
||||
else
|
||||
local data=bin.new(ref:read(size))
|
||||
local dat=data:getBlock("t")
|
||||
if dat.__RECURSIVE then
|
||||
tab[ind]=tab
|
||||
else
|
||||
tab[ind]=dat
|
||||
end
|
||||
n=n+data:getSize()+4
|
||||
end
|
||||
end
|
||||
if n==len then break end
|
||||
end
|
||||
return bin.resolveType(tab)
|
||||
end,function(d,fit,fmt,self,rec,tabsaw)
|
||||
-- INGORE FIT WE ARE CREATING A STRUCT!!!
|
||||
-- fmt will apply to all numbers
|
||||
local __rem=nil
|
||||
if not tabsaw then rem=true end
|
||||
local tabsaw=tabsaw or {}
|
||||
if rem then
|
||||
table.insert(tabsaw,d)
|
||||
end
|
||||
local bData={}
|
||||
for i,v in pairs(d) do -- this is for tables, all but userdata is fine. Depending on where you are using lua functions may or may not work
|
||||
local tp=type(v):sub(1,1):upper() -- uppercase of datatype
|
||||
if type(i)=="number" then -- Lets handle indexies
|
||||
if v==math.huge then
|
||||
tp="I"
|
||||
v=0
|
||||
elseif v==-math.huge then
|
||||
tp="i"
|
||||
v=0
|
||||
end
|
||||
table.insert(bData,"N"..tp..bin.defualtBit.numToBytes(i,4)) -- number index?
|
||||
elseif type(i)=="string" then
|
||||
if #i>255 then error("A string index cannot be larger than 255 bytes!") end
|
||||
table.insert(bData,"S"..tp..bin.defualtBit.numToBytes(#i,1)..i) -- string index?
|
||||
else
|
||||
error("Only numbers and strings can be a table index!") -- throw error?
|
||||
end
|
||||
if type(v)=="number" then
|
||||
-- How do we handle number data
|
||||
local temp=bin.new()
|
||||
temp:addBlock(v,nil,"d")
|
||||
table.insert(bData,temp.data)
|
||||
elseif type(v)=="string" then
|
||||
-- Lets work on strings
|
||||
table.insert(bData,bin.defualtBit.numToBytes(#v,4)) -- add length of string
|
||||
table.insert(bData,v) -- add string
|
||||
elseif type(v)=="boolean" then -- bools are easy :D
|
||||
table.insert(bData,({[true]="\255",[false]="\0"})[v])
|
||||
elseif type(v)=="function" then -- should we allow this? why not...
|
||||
local dump=string.dump(v)
|
||||
table.insert(bData,bin.defualtBit.numToBytes(#dump,4)) -- add length of dumped string
|
||||
table.insert(bData,dump) -- add it
|
||||
elseif type(v)=="table" then -- tables...
|
||||
if tabsaw[1]==v then
|
||||
v={__RECURSIVE=i}
|
||||
else
|
||||
tabsaw[i]=v
|
||||
end
|
||||
local data=rec(v,nil,"t",self,rec,tabsaw)
|
||||
table.insert(bData,bin.defualtBit.numToBytes(#data,4)) -- add length of string
|
||||
table.insert(bData,data) -- add string
|
||||
end
|
||||
end
|
||||
local data=table.concat(bData)
|
||||
return "LT"..string.char(__CURRENTVERSION)..bin.defualtBit.numToBytes(#data,4)..data
|
||||
end)
|
||||
bin.registerBlock("b",function(SIZE_OR_NIL,ref)
|
||||
return ({["\255"]=true,["\0"]=false})[ref:read(1)]
|
||||
end,function(d)
|
||||
return ({[true]="\255",[false]="\0"})[d]
|
||||
end)
|
||||
bin.registerBlock("f",function(SIZE_OR_NIL,ref)
|
||||
local nn=ref:getBlock("n",4)
|
||||
return loadstring(ref:read(nn))
|
||||
end,function(d)
|
||||
local dump=string.dump(d)
|
||||
return bin.defualtBit.numToBytes(#dump,4)..dump
|
||||
end)
|
||||
bin.registerBlock("d",function(SIZE_OR_NIL,ref)
|
||||
local w,p=ref:getBlock("n",4),ref:getBlock("n",4)
|
||||
p=tonumber("0."..tostring(p))
|
||||
return w+p
|
||||
end,function(d,fit,fmt,self,rec,tabsaw)
|
||||
local w,p = toFraction(d)
|
||||
local temp=bin.new()
|
||||
temp:addBlock(w,4)
|
||||
temp:addBlock(p,4)
|
||||
return temp.data
|
||||
end)
|
||||
127
examples/love2d Threading Example/bin/support/utils.lua
Normal file
127
examples/love2d Threading Example/bin/support/utils.lua
Normal file
@ -0,0 +1,127 @@
|
||||
function table.print(tbl, indent)
|
||||
if not indent then indent = 0 end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ": "
|
||||
if type(v) == "table" then
|
||||
print(formatting)
|
||||
table.print(v, indent+1)
|
||||
elseif type(v) == 'boolean' then
|
||||
print(formatting .. tostring(v))
|
||||
else
|
||||
print(formatting .. tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
function table.flip(t)
|
||||
local tt={}
|
||||
for i,v in pairs(t) do
|
||||
tt[v]=i
|
||||
end
|
||||
return tt
|
||||
end
|
||||
function toFraction(n)
|
||||
local w,p=math.modf(n)
|
||||
if p~=0 then
|
||||
p=tonumber(tostring(p):sub(3))
|
||||
end
|
||||
return w,p
|
||||
end
|
||||
function io.cleanName(name)
|
||||
name=name:gsub("\\","")
|
||||
name=name:gsub("/","")
|
||||
name=name:gsub(":","")
|
||||
name=name:gsub("*","")
|
||||
name=name:gsub("%?","")
|
||||
name=name:gsub("\"","''")
|
||||
name=name:gsub("<","")
|
||||
name=name:gsub(">","")
|
||||
name=name:gsub("|","")
|
||||
return name
|
||||
end
|
||||
function math.numfix(n,x)
|
||||
local str=tostring(n)
|
||||
if #str<x then
|
||||
str=('0'):rep(x-#str)..str
|
||||
end
|
||||
return str
|
||||
end
|
||||
function bin.stripFileName(path)
|
||||
path=path:gsub("\\","/")
|
||||
local npath=path:reverse()
|
||||
a=npath:find("/",1,true)
|
||||
npath=npath:sub(a)
|
||||
npath=npath:reverse()
|
||||
return npath
|
||||
end
|
||||
function bin._trim(str)
|
||||
return str:match'^()%s*$' and '' or str:match'^%s*(.*%S)'
|
||||
end
|
||||
function io.dirExists(strFolderName)
|
||||
strFolderName = strFolderName or io.getDir()
|
||||
local fileHandle, strError = io.open(strFolderName..'\\*.*','r')
|
||||
if fileHandle ~= nil then
|
||||
io.close(fileHandle)
|
||||
return true
|
||||
else
|
||||
if string.match(strError,'No such file or directory') then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
function bin.fileExists(name)
|
||||
local f=io.open(name,"r")
|
||||
if f~=nil then io.close(f) return true else return false end
|
||||
end
|
||||
function bin.randomName(n,ext)
|
||||
n=n or math.random(7,15)
|
||||
if ext then
|
||||
a,b=ext:find('.',1,true)
|
||||
if a and b then
|
||||
ext=ext:sub(2)
|
||||
end
|
||||
end
|
||||
local str,h = '',0
|
||||
strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
h = math.random(1,#strings)
|
||||
str = str..''..strings[h]
|
||||
end
|
||||
return str..'.'..(ext or 'tmp')
|
||||
end
|
||||
function bin.trimNul(str)
|
||||
return str:match("(.-)[%z]*$")
|
||||
end
|
||||
function io.mkDir(dirname)
|
||||
os.execute('mkdir "' .. dirname..'"')
|
||||
end
|
||||
function string.lines(str)
|
||||
local t = {}
|
||||
local function helper(line) table.insert(t, line) return '' end
|
||||
helper((str:gsub('(.-)\r?\n', helper)))
|
||||
return t
|
||||
end
|
||||
function log(data,name,fmt)
|
||||
if name then
|
||||
name=io.cleanName(name)
|
||||
end
|
||||
if not bin.logger then
|
||||
bin.logger = bin.stream(name or 'lua.log',false)
|
||||
elseif bin.logger and name then
|
||||
bin.logger:close()
|
||||
bin.logger = bin.stream(name or 'lua.log',false)
|
||||
end
|
||||
local d=os.date('*t',os.time())
|
||||
bin.logger:tackE((fmt or '['..math.numfix(d.month,2)..'-'..math.numfix(d.day,2)..'-'..d.year..'|'..math.numfix(d.hour,2)..':'..math.numfix(d.min,2)..':'..math.numfix(d.sec,2)..']\t')..data..'\r\n')
|
||||
end
|
||||
function table.max(t)
|
||||
if #t == 0 then return end
|
||||
local value = t[1]
|
||||
for i = 2, #t do
|
||||
if (value < t[i]) then
|
||||
value = t[i]
|
||||
end
|
||||
end
|
||||
return value
|
||||
end
|
||||
@ -1,69 +1,37 @@
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init{threadNamespace="THREAD"} -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
--IMPORTANT
|
||||
-- Do not make the above local, this is the one difference that the lanesManager does not have
|
||||
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
|
||||
-- If these are local the functions will have the up-values put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any up-values in its code. It will print an error if it does though
|
||||
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
|
||||
require("core.GuiManager") -- allows the use of graphics in the program.
|
||||
gui.ff.Color=Color.Black
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
multi:newSystemThread("Test_Thread_1",function()
|
||||
require("multi")
|
||||
test = sThread.waitFor("testthing"):init()
|
||||
multi:newTLoop(function()
|
||||
print("------")
|
||||
for i,v in pairs(test.tab) do
|
||||
print("T1",i,v)
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
end)
|
||||
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
multi:newSystemThread("Test_Thread_1",function()
|
||||
require("multi")
|
||||
test = sThread.waitFor("testthing"):init()
|
||||
multi:newTLoop(function()
|
||||
print("------")
|
||||
for i,v in pairs(test.tab) do
|
||||
print("T2",i,v)
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
|
||||
GLOBAL["DONE"]=b
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
end)
|
||||
multi:newThread("test0",function()
|
||||
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
|
||||
-- os.exit()
|
||||
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
|
||||
-- instead we can do this
|
||||
while true do
|
||||
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
|
||||
-- Before we held just because we could... But this is a game and we need to have logic continue
|
||||
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
|
||||
if GLOBAL["DONE"] then
|
||||
t.text="Bench: "..GLOBAL["DONE"]
|
||||
end
|
||||
end
|
||||
end)
|
||||
GLOBAL["Bench"]=3
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
test = multi:newSystemThreadedTable("testthing"):init()
|
||||
multi:newTLoop(function()
|
||||
local a,b = multi.randomString(8),multi.randomString(4)
|
||||
print(">",a,b)
|
||||
test[a]=b
|
||||
end,1)
|
||||
69
examples/love2d Threading Example/main7.lua
Normal file
69
examples/love2d Threading Example/main7.lua
Normal file
@ -0,0 +1,69 @@
|
||||
require("core.Library")
|
||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||
--IMPORTANT
|
||||
-- Do not make the above local, this is the one difference that the lanesManager does not have
|
||||
-- If these are local the functions will have the upvalues put into them that do not exist on the threaded side
|
||||
-- You will need to ensure that the function does not refer to any upvalues in its code. It will print an error if it does though
|
||||
-- Also each thread has a .1 second delay! This is used to generate a random values for each thread!
|
||||
require("core.GuiManager") -- allows the use of graphics in the program.
|
||||
gui.ff.Color=Color.Black
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
multi:newSystemThread("test1",function() -- Another difference is that the multi library is already loaded in the threaded enviroment as well as a call to multi:mainloop()
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 1"):OnBench(function(self,c) GLOBAL["T1"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test2",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 2"):OnBench(function(self,c) GLOBAL["T2"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test3",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 3"):OnBench(function(self,c) GLOBAL["T3"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test4",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 4"):OnBench(function(self,c) GLOBAL["T4"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test5",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 5"):OnBench(function(self,c) GLOBAL["T5"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("test6",function() -- spawns a thread in another lua process
|
||||
multi:benchMark(sThread.waitFor("Bench"),nil,"Thread 6"):OnBench(function(self,c) GLOBAL["T6"]=c multi:Stop() end)
|
||||
end)
|
||||
multi:newSystemThread("Combiner",function() -- spawns a thread in another lua process
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
local b=comma_value(tostring(sThread.waitFor("T1")+sThread.waitFor("T2")+sThread.waitFor("T3")+sThread.waitFor("T4")+sThread.waitFor("T5")+sThread.waitFor("T6")))
|
||||
GLOBAL["DONE"]=b
|
||||
end)
|
||||
multi:newThread("test0",function()
|
||||
-- sThread.waitFor("DONE") -- lets hold the main thread completely so we don't eat up cpu
|
||||
-- os.exit()
|
||||
-- when the main thread is holding there is a chance that error handling on the system threads may not work!
|
||||
-- instead we can do this
|
||||
while true do
|
||||
thread.skip(1) -- allow error handling to take place... Otherwise lets keep the main thread running on the low
|
||||
-- Before we held just because we could... But this is a game and we need to have logic continue
|
||||
--sThreadM.sleep(.001) -- Sleeping for .001 is a greeat way to keep cpu usage down. Make sure if you aren't doing work to rest. Abuse the hell out of GLOBAL if you need to :P
|
||||
if GLOBAL["DONE"] then
|
||||
t.text="Bench: "..GLOBAL["DONE"]
|
||||
end
|
||||
end
|
||||
end)
|
||||
GLOBAL["Bench"]=3
|
||||
t=gui:newTextLabel("no done yet!",0,0,300,100)
|
||||
t:centerX()
|
||||
t:centerY()
|
||||
@ -33,43 +33,50 @@ function love.run()
|
||||
if love.load then love.load(arg) end
|
||||
if love.timer then love.timer.step() end
|
||||
local dt = 0
|
||||
while true do
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
local breakme=false
|
||||
multi:newThread("MAIN-RUN",function()
|
||||
while true do
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for name, a,b,c,d,e,f in love.event.poll() do
|
||||
if name == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
breakme=true
|
||||
thread.kill()
|
||||
break
|
||||
end
|
||||
return
|
||||
end
|
||||
love.handlers[name](a,b,c,d,e,f)
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
thread.sleep()
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
end)
|
||||
while not breakme do
|
||||
love.timer.sleep(.005)
|
||||
multi:uManager(dt)
|
||||
if multi.boost then
|
||||
for i=1,multi.boost-1 do
|
||||
multi:uManager(dt)
|
||||
end
|
||||
end
|
||||
multi:uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
multi.drawF={}
|
||||
function multi:dManager()
|
||||
@ -81,34 +88,34 @@ function multi:onDraw(func,i)
|
||||
i=i or 1
|
||||
table.insert(self.drawF,i,func)
|
||||
end
|
||||
function multi:lManager()
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
multi:uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
--~ function multi:lManager()
|
||||
--~ if love.event then
|
||||
--~ love.event.pump()
|
||||
--~ for e,a,b,c,d in love.event.poll() do
|
||||
--~ if e == "quit" then
|
||||
--~ if not love.quit or not love.quit() then
|
||||
--~ if love.audio then
|
||||
--~ love.audio.stop()
|
||||
--~ end
|
||||
--~ return nil
|
||||
--~ end
|
||||
--~ end
|
||||
--~ love.handlers[e](a,b,c,d)
|
||||
--~ end
|
||||
--~ end
|
||||
--~ if love.timer then
|
||||
--~ love.timer.step()
|
||||
--~ dt = love.timer.getDelta()
|
||||
--~ end
|
||||
--~ if love.update then love.update(dt) end
|
||||
--~ multi:uManager(dt)
|
||||
--~ if love.window and love.graphics and love.window.isCreated() then
|
||||
--~ love.graphics.clear()
|
||||
--~ love.graphics.origin()
|
||||
--~ if love.draw then love.draw() end
|
||||
--~ multi.dManager()
|
||||
--~ love.graphics.setColor(255,255,255,255)
|
||||
--~ if multi.draw then multi.draw() end
|
||||
--~ love.graphics.present()
|
||||
--~ end
|
||||
--~ end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=='\\' then
|
||||
return 'windows'
|
||||
@ -32,9 +33,13 @@ end
|
||||
lanes=require("lanes").configure()
|
||||
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
|
||||
require("multi") -- get it all and have it on all lanes
|
||||
isMainThread=true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
function multi:getPlatform()
|
||||
return "lanes"
|
||||
end
|
||||
local multi=multi
|
||||
-- Step 2 set up the linda objects
|
||||
local __GlobalLinda = lanes.linda() -- handles global stuff
|
||||
@ -85,7 +90,10 @@ else
|
||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function THREAD.kill() -- trigger the lane destruction
|
||||
-- coroutine.yield({"_kill_",":)"})
|
||||
error("Thread was killed!")
|
||||
end
|
||||
function THREAD.getName()
|
||||
return THREAD_NAME
|
||||
end
|
||||
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait
|
||||
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
|
||||
@ -102,12 +110,17 @@ function THREAD.hold(n)
|
||||
repeat wait() until n()
|
||||
end
|
||||
-- Step 5 Basic Threads!
|
||||
function multi:newSystemThread(name,func)
|
||||
function multi:newSystemThread(name,func,...)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
c.Type="sthread"
|
||||
c.thread=lanes.gen("*", func)()
|
||||
local THREAD_NAME=name
|
||||
local function func2(...)
|
||||
_G["THREAD_NAME"]=THREAD_NAME
|
||||
func()
|
||||
end
|
||||
c.thread=lanes.gen("*", func2)(...)
|
||||
function c:kill()
|
||||
--self.status:Destroy()
|
||||
self.thread:cancel()
|
||||
|
||||
@ -2,17 +2,22 @@ require("multi.compat.love2d")
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
function multi:getPlatform()
|
||||
return "love2d"
|
||||
end
|
||||
multi.integration={}
|
||||
multi.integration.love2d={}
|
||||
multi.integration.love2d.ThreadBase=[[
|
||||
tab={...}
|
||||
__THREADNAME__=tab[2]
|
||||
__THREADID__=tab[1]
|
||||
__THREADID__=table.remove(tab,1)
|
||||
__THREADNAME__=table.remove(tab,1)
|
||||
require("love.filesystem")
|
||||
require("love.system")
|
||||
require("love.timer")
|
||||
require("love.image")
|
||||
require("multi")
|
||||
GLOBAL={}
|
||||
isMainThread=false
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
__sync__()
|
||||
@ -31,7 +36,7 @@ setmetatable(GLOBAL,{
|
||||
function __sync__()
|
||||
local data=__mythread__:pop()
|
||||
while data do
|
||||
love.timer.sleep(.001)
|
||||
love.timer.sleep(.01)
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if name=="__DIEPLZ"..__THREADID__.."__" then
|
||||
@ -136,6 +141,12 @@ function dump(func)
|
||||
end
|
||||
return table.concat(code)
|
||||
end
|
||||
function sThread.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
function sThread.kill()
|
||||
error("Thread was killed!")
|
||||
end
|
||||
function sThread.set(name,val)
|
||||
GLOBAL[name]=val
|
||||
end
|
||||
@ -155,17 +166,9 @@ end
|
||||
function sThread.hold(n)
|
||||
repeat __sync__() until n()
|
||||
end
|
||||
multi:newLoop(function(self)
|
||||
self:Pause()
|
||||
local ld=multi:getLoad()
|
||||
self:Resume()
|
||||
if ld<80 then
|
||||
love.timer.sleep(.01)
|
||||
end
|
||||
end)
|
||||
updater=multi:newUpdater()
|
||||
updater:OnUpdate(__sync__)
|
||||
func=loadDump([=[INSERT_USER_CODE]=])()
|
||||
func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
|
||||
multi:mainloop()
|
||||
]]
|
||||
GLOBAL={} -- Allow main thread to interact with these objects as well
|
||||
@ -187,6 +190,10 @@ setmetatable(GLOBAL,{
|
||||
})
|
||||
THREAD={} -- Allow main thread to interact with these objects as well
|
||||
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
||||
isMainThread=true
|
||||
function THREAD.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
function ToStr(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
@ -265,12 +272,12 @@ local function randomString(n)
|
||||
end
|
||||
return str
|
||||
end
|
||||
function multi:newSystemThread(name,func) -- the main method
|
||||
function multi:newSystemThread(name,func,...) -- the main method
|
||||
local c={}
|
||||
c.name=name
|
||||
c.ID=c.name.."<ID|"..randomString(8)..">"
|
||||
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
||||
c.thread:start(c.ID,c.name)
|
||||
c.thread:start(c.ID,c.name,...)
|
||||
function c:kill()
|
||||
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
|
||||
end
|
||||
@ -288,9 +295,7 @@ function THREAD.get(name)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
multi.OBJ_REF:Pause()
|
||||
repeat multi:lManager() until GLOBAL[name]
|
||||
multi.OBJ_REF:Resume()
|
||||
repeat multi:uManager() until GLOBAL[name]
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.getCores()
|
||||
@ -300,9 +305,7 @@ function THREAD.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
multi.OBJ_REF:Pause()
|
||||
repeat multi:lManager() until n()
|
||||
multi.OBJ_REF:Resume()
|
||||
repeat multi:uManager() until n()
|
||||
end
|
||||
__channels__={}
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
@ -311,7 +314,6 @@ updater=multi:newUpdater()
|
||||
updater:OnUpdate(function(self)
|
||||
local data=multi.integration.love2d.mainChannel:pop()
|
||||
while data do
|
||||
--print("MAIN:",data)
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
|
||||
@ -0,0 +1,127 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
|
||||
-- I DEMAND USAGE FOR LUVIT
|
||||
-- Cannot use discordia without my multitasking library (Which I love more that the luvit platform... then again i'm partial :P)
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
local function _INIT(luvitThread,timer)
|
||||
-- lots of this stuff should be able to stay the same
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=='\\' then
|
||||
return 'windows'
|
||||
else
|
||||
return 'unix'
|
||||
end
|
||||
end
|
||||
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
||||
require("multi")
|
||||
isMainThread=true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
function multi:getPlatform()
|
||||
return "luvit"
|
||||
end
|
||||
local multi=multi
|
||||
-- Step 2 set up the Global table... is this possible?
|
||||
local GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
--print("No Global table when using luvit integration!")
|
||||
return nil
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end,
|
||||
})
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.get(name)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.testFor(name,val,sym)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function THREAD.kill() -- trigger the thread destruction
|
||||
error("Thread was Killed!")
|
||||
end
|
||||
-- hmmm if im cleaver I can get this to work... but since data passing isn't going to be a thing its probably not important
|
||||
function THREAD.sleep(n)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
-- Step 5 Basic Threads!
|
||||
local function entry(path,name,func,...)
|
||||
local timer = require'timer'
|
||||
local luvitThread = require'thread'
|
||||
package.path=path
|
||||
loadstring(func)(...)
|
||||
end
|
||||
function multi:newSystemThread(name,func,...)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
c.Type="sthread"
|
||||
c.thread={}
|
||||
c.func=string.dump(func)
|
||||
function c:kill()
|
||||
-- print("No Global table when using luvit integration!")
|
||||
end
|
||||
luvitThread.start(entry,package.path,name,c.func,...)
|
||||
return c
|
||||
end
|
||||
print("Integrated Luvit!")
|
||||
multi.integration={} -- for module creators
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
require("multi.integration.shared")
|
||||
-- Start the main mainloop... This allows you to process your multi objects, but the engine on the main thread will be limited to .001 or 1 millisecond sigh...
|
||||
local interval = timer.setInterval(1, function ()
|
||||
multi:uManager()
|
||||
end)
|
||||
end
|
||||
return {init=function(threadHandle,timerHandle) _INIT(threadHandle,timerHandle) return GLOBAL,THREAD end}
|
||||
@ -21,35 +21,70 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
function multi.randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||
local c={} -- where we will store our object
|
||||
c.name=name -- set the name this is important for the love2d side
|
||||
if love then -- check love
|
||||
if love.thread then -- make sure we can use the threading module
|
||||
function c:init() -- create an init function so we can mimic on bith love2d and lanes
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
function self:push(v) -- push to the channel
|
||||
self.chan:push({type(v),resolveData(v)})
|
||||
local tab
|
||||
if type(v)=="table" then
|
||||
tab = {}
|
||||
for i,c in pairs(v) do
|
||||
if type(c)=="function" then
|
||||
tab[i]="\1"..string.dump(c)
|
||||
else
|
||||
tab[i]=c
|
||||
end
|
||||
end
|
||||
self.chan:push(tab)
|
||||
else
|
||||
self.chan:push(c)
|
||||
end
|
||||
end
|
||||
function self:pop() -- pop from the channel
|
||||
local tab=self.chan:pop()
|
||||
--print(tab)
|
||||
if not tab then return end
|
||||
return resolveType(tab[1],tab[2])
|
||||
local v=self.chan:pop()
|
||||
if not v then return end
|
||||
if type(v)=="table" then
|
||||
tab = {}
|
||||
for i,c in pairs(v) do
|
||||
if type(c)=="string" then
|
||||
if c:sub(1,1)=="\1" then
|
||||
tab[i]=loadstring(c:sub(2,-1))
|
||||
else
|
||||
tab[i]=c
|
||||
end
|
||||
else
|
||||
tab[i]=c
|
||||
end
|
||||
end
|
||||
return tab
|
||||
else
|
||||
return self.chan:pop()
|
||||
end
|
||||
end
|
||||
function self:peek()
|
||||
local tab=self.chan:peek()
|
||||
--print(tab)
|
||||
if not tab then return end
|
||||
return resolveType(tab[1],tab[2])
|
||||
local v=self.chan:peek()
|
||||
if not v then return end
|
||||
if type(v)=="table" then
|
||||
tab = {}
|
||||
for i,c in pairs(v) do
|
||||
if type(c)=="string" then
|
||||
if c:sub(1,1)=="\1" then
|
||||
tab[i]=loadstring(c:sub(2,-1))
|
||||
else
|
||||
tab[i]=c
|
||||
end
|
||||
else
|
||||
tab[i]=c
|
||||
end
|
||||
end
|
||||
return tab
|
||||
else
|
||||
return self.chan:pop()
|
||||
end
|
||||
end
|
||||
GLOBAL[self.name]=self -- send the object to the thread through the global interface
|
||||
return self -- return the object
|
||||
@ -59,7 +94,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
|
||||
c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
|
||||
function c:push(v) -- push to the queue
|
||||
self.linda:send("Q",v)
|
||||
end
|
||||
@ -76,6 +111,100 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedConnection(name,protect)
|
||||
local c={}
|
||||
c.name = name
|
||||
c.protect=protect
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init()
|
||||
require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local conn = {}
|
||||
conn.name = self.name
|
||||
conn.count = 0
|
||||
if isMainThread then
|
||||
if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
|
||||
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
|
||||
else
|
||||
conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
|
||||
end
|
||||
else
|
||||
require("multi") -- so things don't break, but also allows bi-directional connections to work
|
||||
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
|
||||
end
|
||||
setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
|
||||
conn.obj=multi:newConnection(self.protect)
|
||||
function conn:connect(func)
|
||||
return self.obj(func)
|
||||
end
|
||||
function conn:fConnect(func)
|
||||
return self.obj:fConnect(func)
|
||||
end
|
||||
function conn:holdUT(n)
|
||||
self.obj:holdUT(n)
|
||||
end
|
||||
function conn:Bind(t)
|
||||
self.obj:Bind(t)
|
||||
end
|
||||
function conn:Remove()
|
||||
self.obj:Remove()
|
||||
end
|
||||
function conn:getConnection(name,ingore)
|
||||
return self.obj:getConnection(name,ingore)
|
||||
end
|
||||
function conn:Fire(...)
|
||||
local args = {...}
|
||||
table.insert(args,1,multi.randomString(8))
|
||||
table.insert(args,1,self.name)
|
||||
table.insert(args,1,"F")
|
||||
self.queueCall:push(args)
|
||||
if self.trigger_self then
|
||||
self.obj:Fire(...)
|
||||
end
|
||||
end
|
||||
self.cleanup = .01
|
||||
function conn:SetCleanUpRate(n)
|
||||
self.cleanup=n or .01
|
||||
end
|
||||
conn.lastid=""
|
||||
conn.looper = multi:newLoop(function(self)
|
||||
local con = self.link
|
||||
local data = con.queueCall:peek()
|
||||
if not data then return end
|
||||
local id = data[3]
|
||||
if data[1]=="F" and data[2]==con.name and con.lastid~=id then
|
||||
con.lastid=id
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
con.obj:Fire(unpack(data))
|
||||
multi:newThread("Clean_UP",function()
|
||||
thread.sleep(con.cleanup)
|
||||
local dat = con.queueCall:peek()
|
||||
if not dat then return end
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
if dat[3]==id then
|
||||
con.queueCall:pop()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
conn.HoldUT=conn.holdUT
|
||||
conn.looper.link=conn
|
||||
conn.Connect=conn.connect
|
||||
conn.FConnect=conn.fConnect
|
||||
conn.GetConnection=conn.getConnection
|
||||
return conn
|
||||
end
|
||||
GLOBAL[name]=c
|
||||
return c
|
||||
end
|
||||
function multi:systemThreadedBenchmark(n,p)
|
||||
n=n or 1
|
||||
local cores=multi.integration.THREAD.getCores()
|
||||
@ -120,88 +249,43 @@ function multi:systemThreadedBenchmark(n,p)
|
||||
end)
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedTable(name,n)
|
||||
local c={} -- where we will store our object
|
||||
c.name=name -- set the name this is important for the love2d side
|
||||
c.cores=n
|
||||
c.hasT={}
|
||||
if love then -- check love
|
||||
if love.thread then -- make sure we can use the threading module
|
||||
function c:init() -- create an init function so we can mimic on bith love2d and lanes
|
||||
self.tab={}
|
||||
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
function self:waitFor(name) -- pop from the channel
|
||||
repeat self:sync() until self[name]
|
||||
return self[name]
|
||||
end
|
||||
function self:sync()
|
||||
local data=self.chan:peek()
|
||||
if data then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if not self.hasT[name] then
|
||||
if type(data)=="string" then
|
||||
if cmd=="SYNC" then
|
||||
self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
|
||||
self.hasT[name]=true
|
||||
end
|
||||
else
|
||||
self.tab[name]=data
|
||||
end
|
||||
self.chan:pop()
|
||||
end
|
||||
end
|
||||
end
|
||||
function self:reset(name)
|
||||
self.hasT[core]=nil
|
||||
end
|
||||
setmetatable(self,{
|
||||
__index=function(t,k)
|
||||
self:sync()
|
||||
return self.tab[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
self:sync()
|
||||
self.tab[k]=v
|
||||
if type(v)=="userdata" then
|
||||
self.chan:push(v)
|
||||
else
|
||||
for i=1,self.cores do
|
||||
self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
GLOBAL[self.name]=self -- send the object to the thread through the global interface
|
||||
return self -- return the object
|
||||
end
|
||||
return c
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c={}
|
||||
c.name=name -- set the name this is important for identifying what is what
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local cc={}
|
||||
cc.tab={}
|
||||
if isMainThread then
|
||||
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
|
||||
else
|
||||
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
|
||||
function c:waitFor(name)
|
||||
while self[name]==nil do
|
||||
-- Waiting
|
||||
end
|
||||
return self[name]
|
||||
function cc:waitFor(name)
|
||||
repeat multi:uManager() until tab[name]~=nil
|
||||
return tab[name]
|
||||
end
|
||||
function c:sync()
|
||||
return -- just so we match the love2d side
|
||||
end
|
||||
function c:init() -- set the metatable
|
||||
setmetatable(self,{
|
||||
__index=function(t,k)
|
||||
return self.linda:get(k)
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
self.linda:set(k,v)
|
||||
end,
|
||||
})
|
||||
return self
|
||||
end
|
||||
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
|
||||
local link = cc
|
||||
cc.conn(function(k,v)
|
||||
link.tab[k]=v
|
||||
end)
|
||||
setmetatable(cc,{
|
||||
__index=function(t,k)
|
||||
return t.tab[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
t.tab[k]=v
|
||||
t.conn:Fire(k,v)
|
||||
end,
|
||||
})
|
||||
return cc
|
||||
end
|
||||
GLOBAL[c.name]=c
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
@ -212,8 +296,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
|
||||
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
|
||||
c.REG=multi:newSystemThreadedQueue("THREADED_JQ_F_REG"):init()
|
||||
-- registerJob(name,func)
|
||||
-- pushJob(...)
|
||||
c.OnReady=multi:newConnection()
|
||||
function c:registerJob(name,func)
|
||||
for i=1,self.cores do
|
||||
self.REG:push({name,func})
|
||||
@ -222,9 +305,10 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
function c:pushJob(name,...)
|
||||
self.queueOUT:push({self.jobnum,name,...})
|
||||
self.jobnum=self.jobnum+1
|
||||
return self.jobnum-1
|
||||
end
|
||||
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
|
||||
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
|
||||
local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
|
||||
local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
|
||||
function c:doToAll(func)
|
||||
local TaskName=multi.randomString(16)
|
||||
for i=1,self.cores do
|
||||
@ -232,17 +316,26 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
end
|
||||
end
|
||||
function c:start()
|
||||
self:doToAll(function()
|
||||
_G["__started__"]=true
|
||||
SFunc()
|
||||
multi:newEvent(function()
|
||||
return self.ThreadsLoaded==true
|
||||
end):OnEvent(function(evnt)
|
||||
GLOBAL["THREADED_JQ"]=nil -- remove it
|
||||
GLOBAL["THREADED_JQO"]=nil -- remove it
|
||||
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
|
||||
self:doToAll(function()
|
||||
_G["__started__"]=true
|
||||
SFunc()
|
||||
end)
|
||||
evnt:Destroy()
|
||||
end)
|
||||
end
|
||||
GLOBAL["__JQ_COUNT__"]=c.cores
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
|
||||
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function(name,ind)
|
||||
require("multi")
|
||||
ThreadName=name
|
||||
__sleep__=.001
|
||||
if love then -- lets make sure we don't reference upvalues if using love2d
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
__sleep__=.1
|
||||
@ -251,10 +344,6 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
|
||||
REG=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
|
||||
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
|
||||
sThread.sleep(.1) -- lets wait for things to work out
|
||||
GLOBAL["THREADED_JQ"]=nil -- remove it
|
||||
GLOBAL["THREADED_JQO"]=nil -- remove it
|
||||
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
|
||||
QALLT={}
|
||||
FUNCS={}
|
||||
SFunc=multi:newFunction(function(self)
|
||||
@ -287,10 +376,12 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
return FUNCS[k]
|
||||
end
|
||||
})
|
||||
lastjob=os.clock()
|
||||
MainLoop=multi:newLoop(function(self)
|
||||
if __started__ then
|
||||
local job=JQI:pop()
|
||||
if job then
|
||||
lastjob=os.clock()
|
||||
local d=QALL:peek()
|
||||
if d then
|
||||
if not QALLT[d[1]] then
|
||||
@ -311,17 +402,36 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:newThread("Idler",function()
|
||||
while true do
|
||||
if os.clock()-lastjob>1 then
|
||||
sThread.sleep(.1)
|
||||
end
|
||||
thread.sleep(.001)
|
||||
end
|
||||
end)
|
||||
JQO:push({"_THREADINIT_"})
|
||||
if not love then
|
||||
multi:mainloop()
|
||||
end
|
||||
end)
|
||||
end,"Thread<"..i..">",i)
|
||||
end
|
||||
c.OnJobCompleted=multi:newConnection()
|
||||
c.threadsResponded = 0
|
||||
c.updater=multi:newLoop(function(self)
|
||||
local data=self.link.queueIN:pop()
|
||||
while data do
|
||||
if data then
|
||||
self.link.OnJobCompleted:Fire(unpack(data))
|
||||
local a=unpack(data)
|
||||
if a=="_THREADINIT_" then
|
||||
self.link.threadsResponded=self.link.threadsResponded+1
|
||||
if self.link.threadsResponded==self.link.cores then
|
||||
self.link.ThreadsLoaded=true
|
||||
self.link.OnReady:Fire()
|
||||
end
|
||||
else
|
||||
self.link.OnJobCompleted:Fire(unpack(data))
|
||||
end
|
||||
end
|
||||
data=self.link.queueIN:pop()
|
||||
end
|
||||
|
||||
@ -23,9 +23,9 @@ SOFTWARE.
|
||||
]]
|
||||
require("bin")
|
||||
multi = {}
|
||||
multi.Version = "1.9.2"
|
||||
multi._VERSION = "1.9.2"
|
||||
multi.stage = "mostly-stable"
|
||||
multi.Version = "1.10.0"
|
||||
multi._VERSION = "1.10.0"
|
||||
multi.stage = "stable"
|
||||
multi.__index = multi
|
||||
multi.Mainloop = {}
|
||||
multi.Tasks = {}
|
||||
@ -162,6 +162,14 @@ else
|
||||
os.execute('sleep ' .. tonumber(n))
|
||||
end
|
||||
end
|
||||
function multi.randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function multi:getParentProcess()
|
||||
return self.Mainloop[self.CID]
|
||||
end
|
||||
@ -798,25 +806,32 @@ function multi:newConnection(protect)
|
||||
c.connections={}
|
||||
c.fconnections={}
|
||||
c.FC=0
|
||||
function c:holdUT()
|
||||
function c:holdUT(n)
|
||||
local n=n or 0
|
||||
self.waiting=true
|
||||
local count=0
|
||||
local id=self:connect(function()
|
||||
self.waiting=false
|
||||
count = count + 1
|
||||
if n<=count then
|
||||
self.waiting=false
|
||||
end
|
||||
end)
|
||||
repeat
|
||||
self.Parent:uManager()
|
||||
until self.waiting==false
|
||||
id:Destroy()
|
||||
end
|
||||
c.HoldUT=c.holdUT
|
||||
function c:fConnect(func)
|
||||
local temp=self:connect(func)
|
||||
table.insert(self.fconnections,temp)
|
||||
self.FC=self.FC+1
|
||||
end
|
||||
c.FConnect=c.fConnect
|
||||
function c:getConnection(name,ingore)
|
||||
if ingore then
|
||||
return self.connections[name] or {
|
||||
Fire=function() end -- if the connection doesn't exist lets call all of them or silently ingore
|
||||
Fire=function() end -- if the connection doesn't exist lets call all of them or silently ignore
|
||||
}
|
||||
else
|
||||
return self.connections[name] or self
|
||||
@ -889,6 +904,7 @@ function multi:newConnection(protect)
|
||||
return temp
|
||||
end
|
||||
c.Connect=c.connect
|
||||
c.GetConnection=c.getConnection
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
|
||||
@ -33,6 +33,7 @@ end
|
||||
lanes=require("lanes").configure()
|
||||
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
|
||||
require("multi") -- get it all and have it on all lanes
|
||||
isMainThread=true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
|
||||
@ -17,6 +17,7 @@ require("love.timer")
|
||||
require("love.image")
|
||||
require("multi")
|
||||
GLOBAL={}
|
||||
isMainThread=false
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
__sync__()
|
||||
@ -189,6 +190,7 @@ setmetatable(GLOBAL,{
|
||||
})
|
||||
THREAD={} -- Allow main thread to interact with these objects as well
|
||||
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
||||
isMainThread=true
|
||||
function THREAD.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
@ -293,9 +295,7 @@ function THREAD.get(name)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
multi.OBJ_REF:Pause()
|
||||
repeat multi:lManager() until GLOBAL[name]
|
||||
multi.OBJ_REF:Resume()
|
||||
repeat multi:uManager() until GLOBAL[name]
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.getCores()
|
||||
@ -305,9 +305,7 @@ function THREAD.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
multi.OBJ_REF:Pause()
|
||||
repeat multi:lManager() until n()
|
||||
multi.OBJ_REF:Resume()
|
||||
repeat multi:uManager() until n()
|
||||
end
|
||||
__channels__={}
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
@ -316,7 +314,6 @@ updater=multi:newUpdater()
|
||||
updater:OnUpdate(function(self)
|
||||
local data=multi.integration.love2d.mainChannel:pop()
|
||||
while data do
|
||||
--print("MAIN:",data)
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
|
||||
@ -36,6 +36,7 @@ local function _INIT(luvitThread,timer)
|
||||
end
|
||||
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
||||
require("multi")
|
||||
isMainThread=true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
|
||||
@ -21,20 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
function multi.randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||
local c={} -- where we will store our object
|
||||
c.name=name -- set the name this is important for the love2d side
|
||||
if love then -- check love
|
||||
if love.thread then -- make sure we can use the threading module
|
||||
function c:init() -- create an init function so we can mimic on bith love2d and lanes
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
function self:push(v) -- push to the channel
|
||||
local tab
|
||||
@ -102,7 +94,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
|
||||
c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
|
||||
function c:push(v) -- push to the queue
|
||||
self.linda:send("Q",v)
|
||||
end
|
||||
@ -119,6 +111,100 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedConnection(name,protect)
|
||||
local c={}
|
||||
c.name = name
|
||||
c.protect=protect
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init()
|
||||
require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local conn = {}
|
||||
conn.name = self.name
|
||||
conn.count = 0
|
||||
if isMainThread then
|
||||
if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
|
||||
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
|
||||
else
|
||||
conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
|
||||
end
|
||||
else
|
||||
require("multi") -- so things don't break, but also allows bi-directional connections to work
|
||||
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
|
||||
end
|
||||
setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
|
||||
conn.obj=multi:newConnection(self.protect)
|
||||
function conn:connect(func)
|
||||
return self.obj(func)
|
||||
end
|
||||
function conn:fConnect(func)
|
||||
return self.obj:fConnect(func)
|
||||
end
|
||||
function conn:holdUT(n)
|
||||
self.obj:holdUT(n)
|
||||
end
|
||||
function conn:Bind(t)
|
||||
self.obj:Bind(t)
|
||||
end
|
||||
function conn:Remove()
|
||||
self.obj:Remove()
|
||||
end
|
||||
function conn:getConnection(name,ingore)
|
||||
return self.obj:getConnection(name,ingore)
|
||||
end
|
||||
function conn:Fire(...)
|
||||
local args = {...}
|
||||
table.insert(args,1,multi.randomString(8))
|
||||
table.insert(args,1,self.name)
|
||||
table.insert(args,1,"F")
|
||||
self.queueCall:push(args)
|
||||
if self.trigger_self then
|
||||
self.obj:Fire(...)
|
||||
end
|
||||
end
|
||||
self.cleanup = .01
|
||||
function conn:SetCleanUpRate(n)
|
||||
self.cleanup=n or .01
|
||||
end
|
||||
conn.lastid=""
|
||||
conn.looper = multi:newLoop(function(self)
|
||||
local con = self.link
|
||||
local data = con.queueCall:peek()
|
||||
if not data then return end
|
||||
local id = data[3]
|
||||
if data[1]=="F" and data[2]==con.name and con.lastid~=id then
|
||||
con.lastid=id
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
con.obj:Fire(unpack(data))
|
||||
multi:newThread("Clean_UP",function()
|
||||
thread.sleep(con.cleanup)
|
||||
local dat = con.queueCall:peek()
|
||||
if not dat then return end
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
if dat[3]==id then
|
||||
con.queueCall:pop()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
conn.HoldUT=conn.holdUT
|
||||
conn.looper.link=conn
|
||||
conn.Connect=conn.connect
|
||||
conn.FConnect=conn.fConnect
|
||||
conn.GetConnection=conn.getConnection
|
||||
return conn
|
||||
end
|
||||
GLOBAL[name]=c
|
||||
return c
|
||||
end
|
||||
function multi:systemThreadedBenchmark(n,p)
|
||||
n=n or 1
|
||||
local cores=multi.integration.THREAD.getCores()
|
||||
@ -163,88 +249,43 @@ function multi:systemThreadedBenchmark(n,p)
|
||||
end)
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedTable(name,n) -- NEDS FIXING SING SO MUCH WORK!!!
|
||||
local c={} -- where we will store our object
|
||||
c.name=name -- set the name this is important for the love2d side
|
||||
c.cores=n
|
||||
c.hasT={}
|
||||
if love then -- check love
|
||||
if love.thread then -- make sure we can use the threading module
|
||||
function c:init() -- create an init function so we can mimic on bith love2d and lanes
|
||||
self.tab={}
|
||||
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
function self:waitFor(name) -- pop from the channel
|
||||
repeat self:sync() until self[name]
|
||||
return self[name]
|
||||
end
|
||||
function self:sync()
|
||||
local data=self.chan:peek()
|
||||
if data then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if not self.hasT[name] then
|
||||
if type(data)=="string" then
|
||||
if cmd=="SYNC" then
|
||||
self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
|
||||
self.hasT[name]=true
|
||||
end
|
||||
else
|
||||
self.tab[name]=data
|
||||
end
|
||||
self.chan:pop()
|
||||
end
|
||||
end
|
||||
end
|
||||
function self:reset(name)
|
||||
self.hasT[core]=nil
|
||||
end
|
||||
setmetatable(self,{
|
||||
__index=function(t,k)
|
||||
self:sync()
|
||||
return self.tab[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
self:sync()
|
||||
self.tab[k]=v
|
||||
if type(v)=="userdata" then
|
||||
self.chan:push(v)
|
||||
else
|
||||
for i=1,self.cores do
|
||||
self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
GLOBAL[self.name]=self -- send the object to the thread through the global interface
|
||||
return self -- return the object
|
||||
end
|
||||
return c
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c={}
|
||||
c.name=name -- set the name this is important for identifying what is what
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local cc={}
|
||||
cc.tab={}
|
||||
if isMainThread then
|
||||
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
|
||||
else
|
||||
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
|
||||
function c:waitFor(name)
|
||||
while self[name]==nil do
|
||||
-- Waiting
|
||||
end
|
||||
return self[name]
|
||||
function cc:waitFor(name)
|
||||
repeat multi:uManager() until tab[name]~=nil
|
||||
return tab[name]
|
||||
end
|
||||
function c:sync()
|
||||
return -- just so we match the love2d side
|
||||
end
|
||||
function c:init() -- set the metatable
|
||||
setmetatable(self,{
|
||||
__index=function(t,k)
|
||||
return self.linda:get(k)
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
self.linda:set(k,v)
|
||||
end,
|
||||
})
|
||||
return self
|
||||
end
|
||||
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
|
||||
local link = cc
|
||||
cc.conn(function(k,v)
|
||||
link.tab[k]=v
|
||||
end)
|
||||
setmetatable(cc,{
|
||||
__index=function(t,k)
|
||||
return t.tab[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
t.tab[k]=v
|
||||
t.conn:Fire(k,v)
|
||||
end,
|
||||
})
|
||||
return cc
|
||||
end
|
||||
GLOBAL[c.name]=c
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
@ -266,8 +307,8 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
self.jobnum=self.jobnum+1
|
||||
return self.jobnum-1
|
||||
end
|
||||
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
|
||||
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
|
||||
local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
|
||||
local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
|
||||
function c:doToAll(func)
|
||||
local TaskName=multi.randomString(16)
|
||||
for i=1,self.cores do
|
||||
@ -294,7 +335,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
require("multi")
|
||||
ThreadName=name
|
||||
__sleep__=.001
|
||||
if love then -- lets make sure we don't reference upvalues if using love2d
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
__sleep__=.1
|
||||
@ -369,20 +410,22 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
thread.sleep(.001)
|
||||
end
|
||||
end)
|
||||
JQO:push({"_THREADINIT_",ind})
|
||||
JQO:push({"_THREADINIT_"})
|
||||
if not love then
|
||||
multi:mainloop()
|
||||
end
|
||||
end,"Thread<"..i..">",i)
|
||||
end
|
||||
c.OnJobCompleted=multi:newConnection()
|
||||
c.threadsResponded = 0
|
||||
c.updater=multi:newLoop(function(self)
|
||||
local data=self.link.queueIN:pop()
|
||||
while data do
|
||||
if data then
|
||||
local a,b=unpack(data)
|
||||
local a=unpack(data)
|
||||
if a=="_THREADINIT_" then
|
||||
if b==self.link.cores then
|
||||
self.link.threadsResponded=self.link.threadsResponded+1
|
||||
if self.link.threadsResponded==self.link.cores then
|
||||
self.link.ThreadsLoaded=true
|
||||
self.link.OnReady:Fire()
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user