From 7e244edc2ba4fa5fb2ebc48d69d2ee874c893a13 Mon Sep 17 00:00:00 2001 From: riven-lwjgl Date: Mon, 11 Jul 2011 21:46:00 +0000 Subject: [PATCH] Initial release of mapped objects library (v0.10) in LWJGL. --- libs/asm-util.jar | Bin 0 -> 36552 bytes libs/asm.jar | Bin 0 -> 43401 bytes .../org/lwjgl/util/mapped/MappedField.java | 19 + .../org/lwjgl/util/mapped/MappedForeach.java | 52 ++ .../org/lwjgl/util/mapped/MappedHelper.java | 181 ++++++ .../org/lwjgl/util/mapped/MappedObject.java | 185 ++++++ .../util/mapped/MappedObjectClassLoader.java | 136 ++++ .../util/mapped/MappedObjectTransformer.java | 588 ++++++++++++++++++ .../lwjgl/util/mapped/MappedObjectUnsafe.java | 83 +++ src/java/org/lwjgl/util/mapped/MappedSet.java | 23 + .../org/lwjgl/util/mapped/MappedSet2.java | 30 + .../org/lwjgl/util/mapped/MappedSet3.java | 33 + .../org/lwjgl/util/mapped/MappedSet4.java | 36 ++ .../org/lwjgl/util/mapped/MappedType.java | 21 + 14 files changed, 1387 insertions(+) create mode 100644 libs/asm-util.jar create mode 100644 libs/asm.jar create mode 100644 src/java/org/lwjgl/util/mapped/MappedField.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedForeach.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedHelper.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedObject.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedSet.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedSet2.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedSet3.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedSet4.java create mode 100644 src/java/org/lwjgl/util/mapped/MappedType.java diff --git a/libs/asm-util.jar b/libs/asm-util.jar new file mode 100644 index 0000000000000000000000000000000000000000..499d229034ae6b6b753f0c5e9aa40a8e2080c4c9 GIT binary patch literal 36552 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 literal 0 HcmV?d00001 diff --git a/libs/asm.jar b/libs/asm.jar new file mode 100644 index 0000000000000000000000000000000000000000..334e7fdc7f08e1f1b10f9871ddda951dfe7f2d84 GIT binary patch literal 43401 zcmagGQ<$bfvn^P*ZQHhO+vZocZQE9tZQEV8?Jjm1r}xa9nfcGN=gdXE$g7O?WaNs- zT#-t$pkQb~KtNDHlycKN+Yx_xQvUtT|7-u%D=)4lOfRhUEMVHej3 zGsJ`@{vz~!AYUvhQma&^s~(D=4{%ak(u8AUB##pP*>y!0UUOkMtXkI3>vYVFt;nxk2Y%#*9c3;Qyg#`=3#=|7AR&utLt`#1Pz zU?3pse{Tn8OGXD18*@`v4|5YnV;4I{VS9TAS7TRe2YU@`7i(7sX9iP%v5QMuJU`5k z5K`D4R!Zip4N`^O24 zQ7FS8Ot|b2SEP#nF<@ML21AFO1R)|xjX9;-wR^SVCi)&QMqaulGKoU3E`^o4 z?GHFT71rvQ@u&THqMz2g2*@ve$xE28-rG*=~Rv`fQDz48x7y@-5lNx zI~sC>!f#Pd)t^gi2+XBl4=1<`7qAJJS0eTU`cDw-epN@oLi{rx0SJid{|5w3XKPpU z|A~S6l`5(x+K*jR4*W$G)fmS~QRGS@*xeUYqzDr32xTHvB|WiNWLPMLuxx89jn2)M zfPBF!9U zy(kJ_9_aL>^C1?PN1Bc00BgQqka%v(@f7z+B-Kcv7O}~j7!!TUm$4NQ=MJBj@Nnct z!KXkG@EG(?u#J^K?ON*X;L~gO8y$<8Xdz3f1hC~uk&8yji6}bCCUuG@7cUYrTYk@V zj5-^n`$xX~?zx`1U>t*v6=ThET! zWc@q%9Nz}yhtO6o#*$=L{)i_9KL!rvioiin86Q|x#$wPfc0q;X7n&|bD;ii7SCmIQ zF+VszD#S-DGZzSCPLzfdC7DumKEXw<$1*_tw-2cVd^Eh|BAt%ABgF%5arl{zUlc?G zZnW9%UXCsgt3=Hjpfzy$#=2M~iPX?!6gvb(l|u-X=*uQ?g{0JVR|u=6?syokdT|{f z^^T23M0tei-L>@s^Gkcox}w_T!Vg)=xQ!NsM*c-?pzCl=LRs=<1Zzc%0C(C>GQ75te$mSS8(p^fQIX0dkbIvSJIm5G;W)icZ<-L>%kn&VH*J;LXeiMf3(A5pWtFs0wJ*JnwMzXfEN$?& z(oi8&jhWV{(qKb=kxmdKEtb^`tacD$a4-)*>3nXYs1O(fTvxfw2zS{1^hT&GI1dT^ z`p^JX1|94!Gnj*~ZXfAo6B|KP#SyFxC3o-Yxr-`k0~r>_%%*lq(voK_KwB+O*Ww(>g=CI05pFKYN>_- zKjFptg#F$2csIc2Ri?Zj`C5Z=Dgw5=ZO~)|J zuEzZv+wQzndVdt!)Szr#VsyK6;wW|Gs)d~jp<)$X>5s_MlGkitk(bUWZ&+!!*yVIe z7o18#^j27Dj9B!kQdyD-1a;M6weNSjfeGGpQQBC7GH*FEsax}P>mQ6flb!3{p_LWx zQT#__3UTAhgAi(2?8>L(d0`s<<>>0=-;5dR59(Tl&mXA+SGTJ!m*p1esu@LXw#o`a z<|^EE)p!*a$11$RNWKC=W&Is>&O3_c#?_{W98beSs8~ZwT&uMvpXn4|je;7|Bm+jW3E#BDQ$X}nmZ0&gK4+JQMN9K@qFrcqe%qj2T<(>%eTLqxH8SwJ_y_#Od~%CAjxv@_O=ei(*;e^sCLIysg)`%E z;c0%Mrs|1n^rl~=O<{0QAD!@t)AsBEkx=psnnnOYFq?UVuAl7HZgrP;?fEW8?K)wEinCzu6_I2~FAk^>^=!?+fo9clTa^-wq^G zC|SrcBAa7lyaSf^XV4R1a3&l!i`{gT20N?4U>j=g65Eu2YW_?SLZ|e|d|Ip2Z;&kh zv_OAJ@ch7yzW~0!=CODZLsD9K<%7{{m&N4`pyp}grSRGQjd6l{<=yx?HLnSuVzv}@ zd_mvhENGqNZ!yg>@i|cWPR(kWJ^yM$i*PM$ted4kT z7Q(Nr#g2vLw&>=(4A;jBVJ z=ELn{m`eVj%V*B(tyQ01ODsG@KBo+9y`=67mfJrh-qZu)!< z+Rh@Lb)ru<^DQ5xrDLJ66@^Y;Gv>7CQ|@%c$dSs^F~umZT|Jo$bT z2%{Oe<^i(M7J(zUU&pU~4m_AYP#rM};4hAYVL(G5;oz}dg$I3s5e#6=*JD5sJ9kGx zIZdJX$q-s)P6;yfIe_gu!~`;p$wOU;}Qg= zrz(fs4;I57)kQg-iI87tpc*oVb~qRPga$x+{+V%=$srlDKHYWs?faOw*UOq$(7opl z^mG6cB8Vfqc?1`NBtJ%h-DQ^OuP?W1^;>f`X&w8t&w6D3P>`S3xo* z!nV7Z6LBSTq4H!VM9zdfMOSLO%}sO?g1q~x?Ca3Fx*=5;i>F1@!`1ze(5TE-wv*g8 zRD&z4Kf~Opg|}}58ioZfNUkVxBXh3aPDw!W@NHDic-v(DQrcDq!NktmbZ4qAd02*{ z-H4tAE8xf^aqW*TpQX}{Rc=WlaM@(;303(+Pn^iYlS$t`#%-y+UYe7hL=2q%7&N;4 zU8Pm{L_`S<8x@TRSMed%CymY^$hin7;csv!n2L~dAwDCnGh`_ot1FF6@?qo-r&Ge?NnjMm!y5Q#%sP{|) zQv^J*ybHh*Hk#U3IG9pmVSH1Tdg&Ex$rtg=D*PBHcHxF(HluS;N~{JavTm>t34=sj zkkbhyWSc~t(_4+;H)wkofz;mRl z;+;9!manKLM(Q%2@3ZpFn@FPl!Y0j@S>SBL5y29Vjt2@X^!-9=oQp2}`Yo%Uz+W+A zy6*gSF$GTkLW|@F@CxV2&)4{D8A3E=Q#13Uz*!37nz8L2C~Ij|1};xQ#WPb@3uTEt z?W3##*ec4;=;#`|ALtldg$&(5e8`Jka@8eLrBwJBP+cq9HBy}se_H*tm)d|SG`9bg zC@_cak*N?`&nj}#P9qqn*esITwe(qFPDN_i?BVN6istoJrLokinw{+2Y+9m8%=_hq zUtr&BK9d+)l)ev1Sh?L3LXE;3G%@;%7kI5{sMq>_ew^yWsf_$+eih#|Sqa`hKSd&M z1Orlu#oE$@`x==8wC)~bwwdWX4T(Pg7iXkGDF`L|M^`D}KtN>wtuu=LSIQ@BX6*QX z=RSEUx(+D9XyZS_3;@`rMI9gE^VuqWdwmDSXl831c`2Qb`BKF9mH~+NMg3;Ly{&aI z^Hm7p0HQe`E1c|#IJBJe>2=m#=kL3tv-Vpcgu6^(*6B02`WcJI*CB#+jk)KpRc)*e zgLdY)li58A(;nOOrPZ({1l$u-!E>Fy&)5AHy6yn$Ue`Q>v+bMiB0@GS2FuxW zH9wIqR7&3~!|8Sh1ysG6gGn{BrrRZKznqj@L(0GI=U)C+5p}n5A6v{R1Bt~gbs~}& z?HQqXf3N)m&?JiM5liVhjUwd9=KsOsiYy=A8$VKQ6@HGJ1?^M5|uovM&EzDWd)$O;DdUB z%0i@bI~(D}Xzh0dp^d|wa@m{Yo2uj02p?%h3^oGil?1|#)0dtp54mHA15O&E@QK~S zI_O98kvu{>Xh-;opTn3MW&OpO7B5J|G~ORArRSt4FKpqdb|jmjRdPfRKe=3y^HC`48UpvrO3dw}CG2Eg@d#XU<$(OOG>fuxlz^)Z!RA`@IP zOR!4AX+$e6-6<=xv5D7K6F4VFD=Cf^S*60zsDxQ>KGGaj5ghpQKv>_wY?qR-wlGfH z+cs@MX;3K0aXujaWNg>ITAdmbMTM$$C0M6*)zWXduk~`}>gfG>h~LuUIjI4- z#&<8^JL@r<)$(^PcXPn&=Ek@puuz;`7rtR^U|gVtPpedO}9mr=!hgXD#dri5^UNs7J5*0iMLE<4E=&D@9ETd-wj81!sD>_=8o6BVM28G203D z-BsDRhurv`)tWQ$@oT<6zn*4mURj-?}n2@AA`q zO#AGNkVGH#x;~~e!HbBP*c|p1z1=>wjd|Tv^tubLn4uK=9BF9!lnw96sG1%YOjEp8 z0AZ|T7~^hMOj#!-3sVCz&c)C`x#@L*9Ap+UmYQ6Zpm}-b=~PV92@5f6q7sX(3VF^) z_mOf|fnR4P%LO+d_ZvMDMI{YIJvGCzwK84n(zJ4?yqXS!Z+m2LrAV7IRQ)z;g8fh* z6cjm;c#iqi!<61ARNXtc+#s@P8m2)K-qx9644I`oB|SL@PPggK{RtclD|0`Mx4u~e zG~`DShu3NY@hC8xQ84cQ_y=Ux<2Yf)=0mzs38sj`Z-jMUYUW=q3mpMEoery!khgwU^n|;~4wRZCO za9BKJD@}E#f44LojEq`FOcwl&mC&-V)mv)qu6Dz1bh}L#+(NqBB|mscwtVJu59tDG zuWxRmBuwCs8tEH7T_d=xqn%fv_;5QZG4`KWYN>gI)E9IcUM&RMm-&5sHZg$1h*22kf!i^p7beGUK;xh3Uo@7Z0NovZK z)+Ibix=Sj*adD_N+D#Rita|F$ga3K1)?GE++HQ+3mTJ+gHRwdn;8b)~VLrCM-9?TW zf&5tLC%@?p`N~^RU$_b3)!0Kz7=utkmxg`Hw$}meeG2yc5S=Ypt=pu(GzX+vxIMX~oxU+aKVdsQ_#-QN+0HuFdG>D063vJI09d zP%l?5tYMb*9>+huNItthKi?ER!zcYAu*TeqN8CKV-p0Og5P=S#iFMb0+6hS_fcd_< z+XzzOExxBvlf~VTz4^uu=`LyH8YL=*6N@s^iufLTGtD^c`{|OROwwPL1SJ$g&3Y{)N6 zxfA;!W)tt^WKUkpQkgk+8xm0;MEMt`hEm@ILqn@>hQTIGqEGmysR}>;#r(?f$vB8} zNNz?-ITo+W=9Wyuqi&vg#NK@8xu?UMuuj^Pz?wepq1Zgj)Mu)4qP%)aZoDeDXy%JF z&xu#xR$CsMSJh&NC#DKfEL4w$ej1Bsmt}SoM_t;F*J2Zw(D&o-I8e4Sa{LI47UAE` zfsvwTh;>u)t{}p=mD}F`q%c}M36exPzAm3oph(^ZA-)3FZ_$jYsmKoLR)raPFGq`1 zkOdH>mp@M*lk?E;G%84^T3FZwrAbvw#)3U);UG9l7kaKqNMmu z9IdKN?COX3-Eg{(^LofQS&wc`$XhAr1FCf@h^i^QDx~4wo4T#_pCk?@0HmAq36&f2 zn3;HzmNr;k=Lbbp-iMLXZL$i*B>{>qU{16?-n<@VpCW_VHypWSZf+eC5%4@MSMx=~ z68Ib!ckcLYmksg}4G4KW*Ym~|2s=Da7kBpf?`~N02NL-9i^d5EoL7!92tVb+XZnuw z;1POv)R=JH`%4U-^WZ-h!4Z0iu7kr2o+ID|PJ@>EcfVi-&VtAF97n)^PJ&AqxK4wS zF=M;-J7CB3?mA!jp!CFz=!4uK0O7xY zZvz=Xccp7>p~VWv18;by%KrCb%ebB3ETloyj8f|g}!Co7gaJD&|mI_ zb;rIl_Us4t7#0B)5a~!5a)a20m4$!xXn=b~)G`}DYJEoi>km>ub_e7LilB4_i$oe| zffr5#svukqte6b0I3xq2iF=2phI7X@!@4iav=YcDIv;3hlnq>A+y!hI`HoKFiw*<- zilb*S(41&Fur`_Q5SSn|5$GTS8bk}{i2w)M2Ob0ONstZq%2B)T0p*LCj(x|UX+H2W znI{;?0%{Pb0_qPn4gZSF{V~4$1wF&QFRo=eplBQf%&7cgu4Ozx>sSU{k1}n`v=z7} zJ&mj6Ffb?eI9D_j$XT=yXc)K`XgIV5_llm1d$*@_X~Cl_WE7PnZ*B`UvIF4hPX33R zEmRAtXGV_x7zVQEf1$_&1t4qKnDwO-G3w5F+pqz#-KmMKCB1V8xg~R?4}uC(;H0%| z1;ar3Me4{MGzvxsBFjHB2|{pcQVUi`+ORad4QPw-(h7?>X|yA-Xy69x;-f2=B}aO0 zpa$eepf|`MJ~n9+XiNkXXpGoE0f-u+2D+i2e6*Mw(VDBF;5@aUyEjAPp^(-OjF+^Q zLzW5@3!{bDgCszvp=*d7!GqGkvY~58=0yr~2L6J%BcBsDu~69&DYcoGJI1#Ln31`gbS2}tcDfOw$1A_a9r zZOiYIg8dr z?FqUekzqcJ+Ys$x_So-EOa|`rqY8mWK9IO}N^Mzq4JzdKv`O`QVgB0)yHsZK#@y6# z|D|V?>c@@5Hzt4$_1fd@8QSZ==n}g_$Tjsx(A*lhj(;#Zz7g^|efaEeD1C)RXGe_f z@Ex(qV7V%6J9wwdg5?O-jtL$RXaUEHVNw3dLSt>7RDfTS1 zF3(!$k?_P>*NIf{QWO;B+5*NQA10#XII}y@MnYpqWz;TcehVXDh{yxzhClM1K%MSI zIN#*Hm3HM=n46kbi!t56eS6bnUW{$OFXa#}w2Dc_&yg^@!8+ zG1N!afPTm+-vGWFal!>Rs7n)Nf>n|7M2`XcR9|~7{)Qvp((p0TsCEf#Y9*mLV%BD* z04%nAoX=%^BU!J!KHc;NC|j^biJNx(zVfs6fhi)Vfx_a4(*f)W!NFYJXn)=*i2}ITh9k70@v`OQMaW8i^1-y%^&g24%s@kcMWrd2H_*34M67ObaA!? zV3K!^cWOmnvrJ9Ai95s__nt6_ zFHQ!5xFh#ERD_is>PfNPY;2?U=w#bnI}gtF+~8kE*{U6w+M!*~p z+!=f?7DF+2odL?ZVO+5C=0(|hh_=n+NvriWrd zYMDMFEpTZIw6FRhcFPV!IQfcrE-O017x=;J!1dfdoXQzFH+-27b7{}6bG6B2vQ90EK>TT6?@Sxs4EJd@ZysU?WlN3=$wm)R$W4B@D zr@|36Atf+!)Qwmm*eY+7vtMJ3qR*?&4}Qj4A-VG zldPRt4hq<&KUW|IErjJ+s&j$%3cT=d!Y)_K2MMPE#obr!N%+DV1Y|(=s z#;h-YAZIkB9UUpb?~n)>oxx?Tz@5HqN7l6L#=fm0l%IoPqHiM$Ut=o^ z!da%gM7gl@4%mXr1;)NZ!C@6Bz5b{hFoAsm8xVn(35o4K7mAg7C1wxGuG81>dr?4^J~O|M z(m#(r+U%K_VSX9ZIv9t~W`q`954c!D?)o@`ZPeuow=vU-?a+udA5HQzoVAdeKTF&iKcp9K6xw>nz2ZM9VN|k1e{$~|x$$8~!eN`r&jA=F z7AP2)U!<_iv{5S+Q0M|5Ajm_O)dO4~hz1RW+jMNa(h;ITJd+kjb*-v&na!ij961@K z?f~9o6mr+tGSR7A{;}_HW;J7tT#H*4+I$mp(L3Yj!Wr=4YzD?d>(U*ndYcKg98;g?6SKV?=C}1JiPltY@)JgaD4ST!;j2%h<1c z;os|wg?eowCr!>%<}A;!X4jfj^=9J&JIhqhuuY}~b7;aWs5{~&CD$Lm=>sz)vHC{N z*9kD&gdDmS0cyD%1j9R4d{yp8cy5k8_OqkuvLm;AJnQh)$+*4->)DErZLIr`cn6f3 zf@n~8UOMX|IM*k$6U+gb@Q2k+(=-FYD_Uw$XU{r=q}Qr^QXU+uMRlmI#=Q_9wD+AV zed0ede`|)nExfR$9``{Jj~+S8&YnXDlV{=?M0GEopR1Zkjn`A|s^PxN*a#e4_lUb& z8k%AuVTqlfG`9!%UHOa)rxWH!ynYUuKMCNQWX;YNQ$}J-&xh4;rJruZtAwf}yOGO8TY?RIWX$qIC z{oTsLs}I;eioi<9tTeJ#05fCwiC_UR#4%+!+Pu&vQ%&918uRw*8?`pw>Za{BH>)7s zt@HLP8@1weuH3Nnt8}t>F&p7cq**rg=k+vK+kxWHKK*pQ!PrYvVP{T1sf0$#tpVwo zzqqFGMSz+p~=FXaQHBdO# z7wR5p1)Tlu2%;oV_phjyJUP z?|d*`yOsJxG`5GsNmlo6Q3cH-KX;>*|3#9Ml&jiuWSS*HCn~~27W>lSCCJ7!TOOJc z9C`Oex%RDWowuOhPg9dIo^pe$P+@@3(YLDXd}5m!mhX0*Nw-85@3Us$pCaH6j8P%h z5t|xk`FU8nuJ8qB958+Z*&=@CYrk`kv4v5cg~2t9)SzR*OxVa(m;ucoo)(rt>`7lU zSN5C*c4vf6H?pZ!P-D%~y=tTw30HS0F;g19>eYk`$yn1RrX-IPmu|~m!yM`}&J=H9 zphaWC-12I@U;-_fHxvMGzW91ErgGD2 zUElw!Z#5uduqG}I{g#jZBx*#Zy6+^CiyQ97=8?yUb|Mg{tsIN2VR}8lM@7G9LceXYq782)NNtxXrVUChD@~U@(8tBe%U~ci4BtgGfca~< z7{cSySFUTaxtUecksp8X@1dpY$5;)kCuku2OAvi%0^|pB=|`hqh3~$0hE$ipv6K+8&I0k8QluzrTA;+pOJV3ooMH z3yAX*##2gfC`%`11g5XX00bnPDQ%Qd=Xo3E76lj7U8L5$nSSMKOD6kS1_~>we6Pzv zFPE9K;wZmM?KrLFlG4z17Tc(=N)-r~SCgNJ@ntZ9I`}*js5~M88>j$#)K9qSTfX_py$ zY0(Z^l~RPJo@G!7vBw??0R2M%7R~q#Jhg5#b5j_6p;%K$V#T;K40ScF%ocWjaps3i zyQ*9=wxW-w(_hNVbSg!m$J!*nQ_+<3s53Aof4y(Kzt?)Q;$O^dt@ z*ukzqdtlBkwxhT!rii2A^j^KE1Y>5s^ z>qj^cJ)6$G=Zv0fG^<3%96M~`y4!~+JobeJj_**%Fo?q}_sF9UWSMb32twr05Hi&l zW98HrLY1U?wORH(&FmC>T!Vx#_=*;Ea8$7EfvTSveHt`wg(^QtsZNmk(8{vvg{ghe znK%aYxTpwfe_e{vNwdv~159nq>M5@2{n+?te7R$vH62BP`vUB2EDP2D*>~7Hu_gow zBA~g?D!HNudlz-c+N_2sh(qNTUd`6?(jO>KuV;=aFDAhAyVkS8icsAf;?DOq{qhHS ztMp2IMe|mTw_9*yIGaI;_5N)|{CIr%BPb>zP4yc=b+{UlUODa6>ngxemsNMsALyPk z(mIN^D;@(p)gzZnfn1)138fhB&%X+A`j8 zR7y}>aNQBZ8_wLmznj+}g%53kN$6@QGA&<`{nknLK?7s^1X_TSPbu&_4prfcC1v#< zXa;OMwS<9 z*p^BYV+z1P2P0o}qbD(m+*U5p&-{x^o0QataEeFt&3Z%I;(Np?p}C??I;vml@D2MO z0nKCKtghuXHA4b7}CoR%JpS> z^^$AuVrE>@;P>}8r_8EU%xZ@*2A%xH>}5hOU*=gRdFEqtg>1AX%|$tvl~4xK@>cca z+3Gzt^{S~FKkBR{CjH47RhF`psqGAucNOnK+SK`zqtJ58(4O6#XLaK?S?l3=mtg3* z{oK;Fw_kAz0Kw4{*>-kt8Sz>_U1+YoW-;2A^xLee-WL~{YdAHU#uF;BAN(GLP}VQu zG0^X>M2WMurDcQ^$}jdf5cw$0eD*^{aF!Te(TjHqo8p|!rLw+;vM9y*_+?0#-qi1Z z%k>4)#hP=qU~}NV9fGB%)WfB)x!P^|!##P2o=~6aPSyjnZNJ+wG63=M^sv$5$0TG? zVt22(&uyz~Tpu=@QGK8{!acP#^rgX>yp&d>$6hb)jTqIWg!F<-5^9Z=Y-=8`>> zfIsayMUg(8)Am-={L5*22@5p&(fxS&&Mzi*=)^?PR{t;XFUMbUy?hX8NRZO6LH{>{`3Lbo zbuc%aloQ1NwAbqYv@rjhqxb)iUgs%KD-1Is<(4Hp0o!@)A#pCka*HZJ1)){Wi;D?p<+3sUf9$FpX3%C5oa zU+9wJgD@5;%w>=6E%Xh$R54YlmN-|j*bpIHj%pm}03^wC$j`lR%qy1J2&DbvTN>inl1 zOF6XBcFN&;^g5oQq+8A_lHr6ecV3f87c_5lsGtB%zxy!4T1i?Kxt=p`VZmN>?VsuS z*+*C@&V~%31F`8g1RbALXOY$rgHJR_x{QUWY`HBO(ilQl1(rH3m1i^z$q`Q zi3&Z25<$x#!D-DV!bzL4pi-g|{UVwYwHwtmT+pE29BXSE&CdGOSlBwYD$ug3xuQ{f zTeW6eU;knA=lx~l5h|B#qSxc~cmIpFPi^n&rcZ6_3$fzo<6wvwn_-W+PjENKA6Oxb z;rYb4rbxh6M0j|vWv}Q1stHNsdbX-L&$>d*_j=`t<%bk#P`>V-a=u2`IO zg>`Kj)vzZQF(&+tYlw05*Z6KKi3F3)S0kev7E5_di1Z6}x9=OJaT-9=SxC5R=&>CR zCz5NhAY7mr4(=TbCM$8ujG9a{M?KS8phxL(gzFpD(>9tE7{k!I!deKB&Cn%ap z)-%_Ws7sD=yQk7MWRLT4N+LMyO^8%W;_JtBFLyb!-WeTaN(NWAP%mTH$Izd~s8?P_ zvIsL~;6KyWb6LJU$b44O`AgR^WB1M1uSRRRtk*Z$hjBNIeA4Ud=o{#3Xw1%eovkj@ z>AH6a!8_FN$2^2aqqm;r_g7sWUZ$D1s{ajsA}eaU2Jvsz6w3~onQH%6MrjLkf#Kyg1SFn-CE!*8>qGDuXG&-ur4qur;SwP>+pk z>uBcw&P1JqEi|NwzVdGJuPS*HBAH}6tpXmFvL zkRtk#NE!zK8+fs4Eh^l+W1#7H+j!STL1~zLc)(@25*d=Jn4Kt@4yM((5^TUvE0`&I z((|*_zj26)Cl=nz)xx=Rh;Q+D3UULz^-|2h;v$5~X~$7t82FLlu$CRu$)C~{r?K|2 z&u>(TbjKNP4N3Fz+2xt%N5QQR4lSgG)YPtU6F%S{zJ%`_rn zzvvpo3!y3wauB*-na5enl>siblc+Gm4UFCc@?*37T=P_7~`wZoB z6#*!+C<$4b*Eu>i>(FhS_(o>wXJylk?3lshjQfQSRQ*8+1@6!5uX=~ery9Cqch5RN z!!Mh-liar=50_p31^(}Gj#-pMDWzk{oUdHqIa|B5sp4W`*Obb(rsR6Z=Z7<;p~(y%;M}6FRZb@ zhfC}&pX=iI>6Q@0*{_^)#PyXdc(8Zrmk`>zCP)Y_oLAs{$(4ZO>{Ktzv3Dt!2;^N( zfZhQsfaHSw0#XC10y;Zy3FbI=6==O^31?WT3}RTdrVn$|f$TtXL}tH0BgbdLPK3<_ z;UZvy-v^xmngN>u_JuYAQh>-s-~v^G;eu5{_ys`+Ob1Q}ss{xHlnsmvgq+7yDOZ6t z3p%q_83W_GDYgVm32FmG*PTUi(Gms&EDJ1mRbI(sR|d#qD2V~Oa_h?7=Ftb9YY#H# zja#~qdAMo;g>4MM0T#}yD_(N#%BHw%L2ZC-z?E%^pg4Dh*Qy)6ay$1!@2y|Z%d3mP z_F;a+r?@*8c3~84s)1w!&ITR?yo=3Dl`D=y2Z9J88v*aj4X6w>0p8_u&&xH%igFCl)$P6I za}Ue=qga6~fv1300?F;Y^>a_lImJqHcF_#omvo;D?Y(hLZX-|Zy)(Xd5&cojyp}vxa>TKx~KT28~EvIVL957P*sLF-h=3%nM_~OZ__oq z64sgaP_WssS9Mh|-7qNGyfTxe@^QAW3u@Y-eQQo^aL%PI_T8&E|4{d-u;JD;$JM+= zl>X9|lHDLsp+74F5USIkl>!RXjuff8xw*|hT5LLnrl0vXP74{P-D?5RIVViOOje8CM>h-V+6L8)~{hSueLZ zth2X{_2fV972Ue<1%A4S*3Sk_TZbejuXxz?2RiJ;ZP8JCT>6t_9t;^Un3d|H0XXyx%jga}y+8 zuTd4XSJZ(_{v8yZHsuq*6I-CpB-Le%CN;_n14&KUnH~nsjD)F4E*_w0NweVF^eFgj z7~SZk*}P8H|5TXR`);atEhodT5RN!6)mks>hFxc?%74Lta zg5qOn-tQ2{$5c-*q!Gl0RBuro3&zAaSu0cci89K6iC%i9 zwIpNt2MjaB?7CwjY8|^!5eLq&XwUYg>1vLQ{~bT?7nh}rIoR1U@H^e;q^jgA^h=Cp zGD`nJDJS{Zw1eBPW*~0>w5cdaQTG_AY z+gj&w4ywzX{+WytvBoT=>aiJyc>ii+TDi7BX;1C+gwShC>&`Nb&VRgdfyNs)J@Ll+ zfq3R&HgBNPQVlUV%|_VeS1fM}pksG7Pz%wd1mBkU-5l+%jnFht#v7MwnS3lwVB$^S z!RvCp#aD5y_}Ek9S%+V0J%8eK5Y{SfK3BP*pHY>*DI+{N%;nWtnJk zJaoek?Nr#f4(;)bHI$x~hV;Ube%K28E`zMz=f9v??A3~wg!D3OV87~_RL>j_pVGQg zE{0OPhu1xW*Zof@K^#ry$Lrl@qFFaB_e!1YZC+<90O8)nwYB$b<3;m*+^6v1(;Dij zaC0GPFbg;JG^X&(GjOkCqbg`bP;q#Wmp8_9T*oqL%;OY>vr{Bth{Mq(8uwt2S()d~ zhS@#vT0EI~B~uHM(8hDOTW?~-@tV*6)`GURaw>DJrkdmAgr`*?otE^$DsmJWJD&(( zM6^6QrviTCPODXcE*@F1;&GCvP`96NwF#Ly&w{zYT$tYIlP)&V&Z=-r@prk*)}krQHYcj@*OAZOF`PB<+LClsTcCyA?wRW+S9g}k&TQH z_U(_DBPj3NWAu-Cne%z&^Uf5g7dQ4BKH8PE|I@Q8i^CZp8#xFH`lK-o~;T5W3w`Aj;WTc7qWEpye5&P{BhELEw znjiMrWxD^p7`^73NGZ9cY_KJkR!B97Ld3cb9wKwfEjUM;;b$EayCBDzS_#)*RUQQp2ZM$bXk!6fVAeci``!{%>B@G3V=DP%(cPC zQOm&H1av-2!ma=Pd6EUw3oN<2fagZy)8Gf_dy|SwPZwG&YiYP4Vsn`qb87=VPWzSf z4gc{&>=k*$xPEMkm2KS-({!!@CEc_eUYfZ=-~9lKR@FO(ZCG==w}3I}X^sjH z`6?pM`3M>8^!89VOx#43w^=4dAF8fD$%Fe{^Y_{DVk|$bt-&E=U1hm{3E)Iv{m|N) zF3z@o-e_hjbl|YB;P)(jVhgqZ;@k1X3_AhDJmA$nkBu&QeY}#+?CM=lxNS?AV@;0ZHCEi+jo4=F^51T3& zW#B*IrtrHUeGB6R8}dc9E=hC1dG95aeXk{2(Ahg;m-P>-vJ1pJ;eYPQ%m_W~m;S2( zIRDrFl?41Z*A9rAS^i(Ok#@2F$4vtg{mcP4C1VmT&1$eOK+$npP{6z&J7&<(%)s#8 z477topq^AtO>P#RkMJ|5ef=+x131KkI&^bj9yw{+y`=wxuXhR-CFr(vmu;JS*|u%l zwr%WX+qP}nwr$(!eY$T)^xtv%K2$|TMZV0coS8L8j{0)#lX4z}6RAYOMMk995qA~7 zI;cx$h*07=gx-onMDaY(?hoehP!N5p8wHd`$$_!)K+$(O_^uEiKll-GAA%DMJ7DC! zeqlMSb|Mcucep`zr^ARTaMm((Vd?xG_>WNyISR4O`sP~T) z0T;-RAZ!K-$fD5Or!IK-tf*lwVrU>};5HVF)87dq`^5&$*B8qOWJ&Ivxjr@1nUc)h0Rt)Z zt2f7-bYv*J_HfGMvpWnCxtp7fBJ#11Wa`rS92|7M`JMo&Yi6x-G2qJJ3}F;C7h{5q z1LH5Mqn_=V_woevpRv-yhpLK$008Vl{=d|<{~7E5sjelf?kZuqA^ds+?h}*+B^8nX zgf&MyGBAImQek07x2$dyErV2AD8cdU1HeOvu5iC4`37EsiIqpFi1I zc6hn@m!N2wzBGPrZ*OkT?x^j@_WFL}_Q7&H0N3ia?*oBs868FhQv_5RO}r_VXUQZn zde{tZEiytjH)N-6vNfY*OC1;WJ=Yg^bySi@RK&|{wLTQi*fZBmbUltkD^o4JI}mbGoiUux2;c|>D6GB^i)%f z&~8^rD_Bh{Ack?yD`%$piOgbEXwX<$-(s7}GCji*l_eO9ubB>@plkinNWck~O+bg} z_7wo}4rzJ4>pv*!8+(T?Sm&w|BUm}_o_FNBk=4J`V?F%>Y@xBYYz9i*{N>8KY*1oB z{d*>9Ty-RA!szAkX&PC;AA4XMY(4NU169{G-h6g4Ug9t_~5M%EUfEJu#Yz1MEeo!R@5qk&X zfF?vCr1gE>>JBuXUk%}w(YvphcL&xLuO?3(0!7_4Df7dr81>#d@sUhNzj?9|eG>Z+KWVZ zF)HSo%zAE7F1W`}Q8et z>lPQyvK*zz(YVQ(pLXo$5kLA&4uOf)`u&hd?NX&~a<+@@N`AoqIS|Em^s*qq003NL z0RZ6sw^NF!qk+x;H>Xr%*w`yCKX>`uUT^wn=)Vpq9rgr6s3x|)^UsM83K%s61o0yv zK$&OCGz954NU&k|SPL~R#ac?)s02|`LpGqH^HKhxQ2eSev@rXcKj+&#m-cfmDO`+M z)XOe^%KEfrwM&GJs$S7SyeN)K5Vwc6@SGcP+M7e?( z#SL{;vqaI%yl~BpvCdUQnxk~8JVTa+lO1C-6}!zEcP~r1u4>AaT_u%O+{@eDge%E? z1kj=Aa`#y%##Tw(8LhUNh!G%a-V_B+9wexSE(E_5NOb4M^b^kP*~GfCCoHBT%~owC zD=t~1Oc0h;RqDJt3q3t=P`SbD)dsq(;@{yYx;r(!oe<-y(7@qw&x*~?P9&Z3WTduA z5ux(S>o6$-5t23>8w9^fRhc~VCIi%;0??m}b1^@cH>x6gJDQ)n+MXCME!5)-CEqP2 zpBXuwCc+K**c&XYF+R@nS~2(*?XUcaCIyioV+ns`Oa4A-I-8BQd`lr`B?)RwI*F$e z$q(?456{GMcd_xDnx&b9mz|uR0Oc&R^4_9Wy5;4}IDb@5(_zqtO&qI8x5BAr+)+x} zCbhTw0d>Q)Cv`nN?s>uhwiM}+m*&@(Yasz_K`y9Gw~>TqimSXpVW|e-e5F8auEX*T z<5PY)?)ZyZ8E5OTQm};ZXr0f)Z=@+f1D~#5p7rI_3z>p`*&tfTrW#=}Z#(FYG$sz@ z9;H8NJmVVJQuzUX7nq|0WE$9DNaZy-?#^~z+Qy@NEFA8-(7AWs+dkiE&{-C4-18EL z6Fl|ZNI^9n#UyRMAw1E4(Pmr)kbC4oWzOgi6Q1Cko+HQI6}w;laLZo@bn?{A1HXFd zM}lQCx9Ri9b+DcH=bF4MIvF9AV=wU36+j944q zVFkgG=*ZN(qG&}k+z>;psUHu&f^5fgf76I~VUtJDs?fGcv4Ek@YyMY6SG*4(e_Qt~ zm{2el`GYVYAMt}BKNrC>WX>r__f8O>TlB0nD2L2>4SF{~C@5#%fb2^u3>~5 z%E>)3ZBxr~%6djl{Y?X!@&~|D;uZ--!}mMT7f#mEjp}bcavgnHQ$>FE;oS$QPrIOZ zyL!CPq5Te}YLMxibvgv1TaJA*m&W>l9Jit5unKL^6p|PqV zF@4dh<}kc4^#zA}unat~x{(%R^)@b?>@C$vB2sJ%&gyu$&o`>R44N1{n}`SNF*4k_TM6i&y`nFiqB6~rfEZyA7DC|{?bBVZ#w&)P|gh$iTGB-C>sGrlGrA zR%Jqo*k1YS&n9h-&b|XpN-_OiTcV1bE&HMsQ;dIGU>s-cXVR5t#xJ(edZ4rA^YsK| z1IhYkc|f@41i;+l>q;BlDuC0?s4+H*k*&VNR0NW=)p(i^!I62}YceU+-Z-55qPQ8ea;h+Op6< z#~%lq zLrM&}QHZBZGWw;-__c%br9s08c3IQL4zs)uHP~&8LVGBJH19|mvJ%*TU{gTce>r4O z)r{k86o!w{rV;8ej~h6ZJi9wJtYbTR?SXc<63>fx1isWLX0AQZYK%I76ojf4;0cJ0 zd*|;DnH_4~3&N2J+1)EU(L)>e8*qp34YsJ{4mhCLkDSL$h%-~F=LR!@4)L2Kh?{pn zp~i4aDb$8)z%l?Z5DAeB&}K%QL`W^9{2iWc1i-PZ|9L!zQ8Hd<9mHUf)r|>Mtj_FT#pXa@yaC4e*S#_3A>)u}WzqT2p zNs^OAwKH7KmFul3n?22OGBrEXg*NCW^?dcxUKZ7hf zlXlW=DRRf1yw>gKjb!)@%(D2WLMBJRFQ@W5#G=Ild`%9bw#d>0H<0n0`SO{i{OXzV zizO7Zldr546G~1#x?`nE9vNnk$*)Dlu})kKpm{9!7QP=GC-GJ9fLui%Q|AvcO(dJ z$Q6zl2RBUB$ZJo(5o82E;Z(!A~=wvFFypvR}u7PLzfyXe_+Q%c^yTL;5VjeeR3-;&sIq{a+_3n3oo zg&b?ioMBYW%9tw5bkx0+j$yWgG){ONa}>zK;=my)wKT(!Oe&G?G*lzz+Ay?k-Q9;? z2W9gV*~jrR#hzGg%#(s1{Dm;AG9tY2w~r_o3YR z6fg2M2v7fDuJpXEh4dzcQc_3=P~RV0$8FP(dB4Fjpb(82^jHdUKMJ=wzY zpG1FRsp{6KVW8nAeWXtOjqJz2Hs?4S81gag2DNY?|Aqd)iV45{gT8-71ld1p7XE)* zOo$uU8e9KwPK8VK|At|*{8$x4uc=oAdjSlOh!W}(uGvjt5itZ{JZY;227}+p9~eWf zox$MS?M+X4b>qtcC_O=pn0FB;oeH27K?^!ydPVSC1zzJEi$uta_N`%m|ljR+N%YYTIzo z5tO+I47$U$5Yf9UlOCM?;Yc7!FbViM-teqR=vC`1v@^C^7VJ>gE3|wsz4Q!*^NFQm z>9+M`TJAVT0Zl02S5wfVny{7pV*=<_nC`(;+jk#g5#ZDeqH)-oRz*m@jSRTEZ^Z5} z5xyv!jQs-fesPhFyeG3bG=9My@?&!L)1FJqhh(`k!z0sQ4o^6^Jeh*S}o z6pRE;3TC_w@o7?;8O=Pv`GOcF2<{qtK~JBj`z*m0pYse<`)UOUtcKN4oAHE{q!F>? z)u<`FHjROP5Q7Q9C<;4_i4^7!Mj}{36a_HFhz4+@S3CI2Wo)1A3jPu&hn|0;N08__ zaI9_8iIgTltZ*?RlA*~gIVC@FEtb~F|eBOz%q&xU3w#z{6raIhs#F(H^#^#lc zHTWvl@W@Wnd$?R5h0klW@L6dq#Q z+DMm*-z}?zBKOcyD;v&d_sf{>@7VCyNq7tOE&Mw6XXArz$^*Y-%XlkU=E6&s(a1Sp zURJoxluwCM*37+Iru_zAzpar}6m@+pIP4DT$tS7X;0U5b2T5!cM60m9cZ6;EiM!R3 z-+vAPERgaenEx_h_@A)(-|Gp|28Jfq|0@lGm1UHW-7tP>)>Hoo%fz9o=7lTTK+u&p zm!K{Yp;?hjZmO62X9QCdVl*&o*rZ#~fWzzZSjugNtx&kM!#wEqD7 zfRxNV%>`A0OiBOdxvl;Ew`?$5EBB9376BsHL`9L$vBAP=r^|Ba(HIw9nq^oMhZ{F1 zYT20E6dPyq<$*ivMv@* znwTXK6XPsX$4(^7qnYR`9RzP*ThIAnjGHXJfV|DUYPGR^*{+oFa+cz?m$JLIp`C5w z#}m4k!enMP63G#2Nk$gRz8L=N;{DB^hP_Fuzfx0J)q)^m1iA z?no5rN^yF0C1^ZDEdteooZeC;SY)1j^`%0EtkHl2DU*p#mESB2KbT%6LRuJeZalBO zHqqiM1)I24AW@we8L3K0+;4^!d9o1I-6kAj@nL#K+&%O-wUA1ZTXh^2ZXljhNJmDP zHmMFuc1oSiDCM+#f2?h=LVDR1bT{dc@ZOco@<=i2T(w_oRwc3nI^4@y7o_uid(78U zUYuY=BOgEMOjGMcB&p`-g{0ZCrlS+NhI4IoACX8cpQvl7=;Ru&F%$%riy?|R^L7X5^tTFo$RdOx zL=J=3WA#=Pl=R;qrmQft$>BEz@eaR5mb}?K=z;D zFiGL$HDKdgXg0{tAjeYibHtOCH7HujRa>J?VjSMxryt-cUXtZvFaJ%;vFJ8oM5y^C zPvKnJT|ud;eB#B%z(@Ey6S+Z3s6!K+D3>L3>xCW+;1$0I(%QY!J* z=D^CTDnmxm+?m(%WpaLrGA6h&|Ql0i1aUaO$kX7er zFX`4xOVa)2?tGIi0pj`F72lzJU7bWGa3S^1l)LjD1m7P(8Nt%eK=1{&*_3)C9cxkr z*|9&mtADn!pM0GjsUbU3yWx~RnzCfw*`}&hoKzrIpqc}$20OkUWO#163|_@u7f_gH z^M@zym^uTYd#%~MbWYi6MhDf#!4VQ1uF+s}cyXeNGLf++L!I_eXf&dj3_`^-@5-aT<*6e6=_4ez9>511HkuRtU|46n${? z{FbcHkx|f^#JUo}R;lQ_@C~7++7eiP*uW3b zRI0N~TuWhGx=~?oWq5!XJ-a~vWDzp@bOuEZ5q&gKrPt^;R>>^apOA;G4wS~E3Bl43 zQ9w~A(oI!UK+?@R!VIZ!sgrgIwEcoA@fBQ~Bd1{b!kG?aLAlVhX9kKd_pR=FY3f>O z|Jf|{i@JHr4g}W3Gw}b&w(_(?eXI{vR~NBGP2EoYMgPw+Rnvgku z;&EEDb^k9a9+dl*oUW`?P*)t&c8S&vmT}=J7y<4pHta)54pWRV0XMP%-W0S z87+-U&01cfn>>t~Djsu{D>Rd?#a0_DDIWSAElP`V3+OG(MbBXHnBDualgy-7b`vS{ z)izW-UCn}8t(jgA@~WK+|1bg%9?gHS7fYW<;*17eq^(BXwxk`iMg}siBrW_IT){ZA%Ss=faHw z`r~QJP~02Z6Z8v)(!XZ5i}L7#PL_XYJ4(LyApEADqo zAq4tfiGfy({Ue1u0^t*p+2fuoZ1t5Iy+Pzg9U4(Hd5_p9=8xD%?mLBS!nk4d=$A2V z{bRVajoRn#0}iIb=rJ&3;4ykn&l#(ms2jNp7jNgiVNIXI)}uYumh(SSX#bg7w8SEQ z0yK*BJGeJ|hp+kt`&m@44I5-S`HFFY$Hwx7<@jSgmM#*(%THgb{c!gOwO%|oP9=|e zfmxQmQa$}qzla!XTShz+G7;+){;`fDP(an$ZCt~rPz0xAE{;j8kvjVH+r7BhyCd%Y zPKUK%A)0KM+S46tv?xZrqCs56wv_0f5;qy8v4j&|6%o3fCgOwH5HU6fE)7;yEVwb) znjWUk5x*7_zBE_S#y8|peu%1eXG9I&4FP&yn&vE0J@V?n?a3T|p8EBaJ9@s@C}o-N zXfpm+T8!B$snn^Ql zbs9_U#Y43~>LKcXrus8E0y5OUB4Xj+C)EF6s{h9#cyW@B(jq@fh+L68e~&Pwioji> za^Du?OGUxV5abw4w(c4>+qfp$f;HK;{KsnB9r#-zj@ztl2+SW@F|Ws~oY$;Iubu0o z)!WNlfIB!GXxFQd(Zrm=ZzrrU#AjT_E3WE)C?trHLpW^4{=^bcCD%eDfnitF5*u_6 z0uA|F#9Yy7+wua*S7VD+F0$02aPcjt9z)$m@{$Gobz50>RLDp#@-8|g((tkaQ-%9h z37Yk$$xyXzE7x^uH+T`QLLh?z@pT$WP&KVr{6V`-k#IrRdnEyfndpKfY_Fcw$zKAL zo?C%;M4i{$-`V6%IkWzi?Jr`=8FzE-di{6BYTK&>J0Gft_#Xm`KbbTZgiKR`956T! z@-=1--xP3-U9(Z5`PlF#qaL{8;(DaC2O}m9c_Q1wwdhkD)-D&RO?+do{Q-^Ibj&tS zICzog4jZrKS=K6I?)(LC5*YWy9-h-6?;@P+xD1F{$94|FH6a>Q*NLjDF2WssnxoE3 zl1PjzlDNsp1*XW%1_Vzz@& z4;))!wrL$;(!_fGl#bwNad!KF4*C3y5^S^!)MQHo8?R5S8t}rTiXLL z^IO=d^mM0x*hr_7sj2k$M4?>XWf9>*pTA$XjyGf%=l6;5B*1xbl|Ue1z5s)CDTN&| z0)%Y$db^8@$OO+!ftVogV%fSh=67X_rMfDO6(>=}EU=NYY0F#6yLIkWd5aj^YfW!a zBf)CRG?uzLt#?rwsFO2kYjbmJ+Kbw|YxJeQB0f$=$WEeaEUISH>|xkD$Y% z**$yYRr3moe3cVL$!fY9p|(*9%cXK?&PZk+#?qRL+H9eh(RfsDSQsig(TFj^zE!LV z>DcL6lLPGkkcZ437Sj=^PMH^(8s*$a7bhJiM)Tg%KPQ$|-&%k&Qtjt~I6_l#-bRo6 zeZ0oX&t_x@K@;9Vhy8i?Yu?eP__cCLQ5Sx9mSG+v3%MMi9PewVPk+tCB2W9(P6br_ zM8nJiE_gXT-+sQC_`G_BC?`jAX6a08HFVj~8oxAr&$r(pxoax8gZw0i(??16a*^7f z^ibAonVzNMNZXF|u4rp>FA0@T;yt!54Ts9Sqo%1%_9sp_&lky#jmFn2`WEA2$=u97 z(9wSaxrH)g;YRTr2Yq$a>`H#WR6PxprWu(}pR>4CAK%4V*XPR>2Nz?OhXW;+FWB4n z+Sp@x!t1>qm-0SI%-rUXKgi_bfhVjQji@tzAU$To7{1-=32KFmmuuN27YkXxw0lAp zFEUb@S4_1Xki1PF7%*e^Q6E*<`x?VF)PgkvWd-)v?t_Zg)P?O)u!TNnAT)R{0O+ui zX93oy|1=K7p6j2qeT00RPzo3pt9TM_!~9u0x4xkDU|AqB|Yzhf!qpj9FCV4U|8_x-6>EYN@X>cbe`i zrl@7n)i^uGTVZ9SF0riAVL_jah_FbVE-^0`)~C=Hu_d%3%dj82OAi^0DY$9r3Xf(C zBY$dLq^>M4t>7jT7Y@~Nqw5*O+D|iVcnJG<^Yq z&^7K=eGJ30OjBjLw_IyXvqaY9s5WZP0o0b^5~ke~+KyE2wCW=Pe4dn#Hb<#%dV7CV zp|f;FSfFx{TyJBHaCuCf1$3|!!yBs zRPbn0nd@GOmf9JQ={b6Q zfpR(@60o#=$`Y$OYKrJa z`4n<;#cKcLO`F1GmPc$z#7u&4AzE+n22{;G=&b9pwXks+gi66sgfO`UY*5tMtrJH~ zMo@vfY^b4Jp`c2onN2Q)t#&V6v1G8sS^payOA$QIlkz6LI5twLMw{E*(c>|npiNV< ztlc?)Tzn#|5K0N7mG67-)Umv95Wy&|_;3{+2qu-V_2uZLXgO`G$;~IM>FtO)iv<-2 zu?XwznPwYwZX49Ww&3nh2cQi>55Fh*ubDbObea}9oIPM4xBoDJ8erX<-9jDxHb_q< ze|LBfOkYrDKqqFK-FzAScHja{-cDe8JL|BU*g_GH?p4l4x?9z%Et)=_Xd`SYU1e+ceh& z5rd83g+zdBdL1kvP67`$elpZx3wTMnfH@fzgAa-dJ|R0kv2*mNXM%336VUVCAr1jg zxh!yc25~>+Q}FZNV1PSdbpQkZ0Dx0K`rX~(b$~>`ioZ|r3}E_M(#5Kn;*O*c?C|P#&f}$-mcW1-Js>jk;$Apa-swT|itwDr^`2f$*Ko3pu}ONZD6~ z(Eay<>);U}8K6?=Dc8_CY^N9x%n+mR6ktM#yLk{m7Ca1C8?cUE_*P)T>xlxG0Y{&k zpSaJ_-wGf@kb98~C<}xhY0ubS2|yOK4PnpHznJiYE9gfENCC(mtFH{98-6Pk2?9M2 zB@bo~nh?(pg#gSRs1L#)87s3zqDR6au;xl5_ z{Jh_MMxrZP7~(Yw_E!VBy)KMOy%V;}tkjM#L;K2^UYh$LP@2b1=b1{iiG5INC*48D z-7+WL4n{xi(@JA5e6;GR#SWd&k=vDbyllSWbH3&0-18sbn##picTI$m*O-{6)}#pzM9h$(F6owCQ{4BzM}Cupl8o zCxIwi0COTLiz%c_ftb9JYg)?i^~r2rzau^e7Ze>LMesu^Ji#)!N|*>Nnhk6!279$R^fvwI%)fzwfQcbxV z0%LR)^{Fnv(ZsVxXB)yXYL$DVk{*p}R**_JO5qc;t1Vqj`+@4Z9DR&7*8FP2u-$}w z;Pvbj>T0zWjWDIWA-=Q_#?ImS# zh+^#+hIQ|Vny8E{GD}V`e%HuM{J67{t(NV=cVSku^HCjk`wXODNYsGC&)Z)gwpZV* z9gvn&eTXP473w-qyYgjd%j+F*r2eNN zx~Cf@-@d$SQLpUr^TA!(A^lC|_&KVP|Jwz`TcGI54H45x0`oP{S?M=J6HAZ`QA{A7-7Hy~)x@-W^ zNy(cOSEyv(=_j8(&Rzcl+%@4h_1y6tzFN($;zFgpY}1fyOLt<6Z&9q|KU$*mNBm|( zJDAj>9HT@`8=XuJYNgMp_`sN+6A zZCUhG;IE9cDYIg~EjlJ%H2Xi5G?Af+JSxhhsm8F0WCBw*vOXu%0zzKoiHP`dElA5Y z<>knME6ZkkEHs_B#1XE@uQ1-T1Z&-xT^;Q_`}6vI!WEqglO*T5!K%Jd8ta}JZ(v8& zgqT>vfIXx^p8f`d9#OwjxvENM3tXgo=5>C&;}O2iYDeh2w&9Tx=krz-ED0zy8EBA^ zw}O>yoeiJayW>mx8se)Fs1&u-RaQaPhTM`q1>o<~;? z^&j@NIICAlGo&>w%#1Ab`c5yo#Hu9{7Bz)&&BX)ORPwJ6W`JzGl|6xyfrI&wrH%f3 zcIFZ1iEYTZf=0llCGMnlrZLsJ<>$D7s(n!sW)4m0Xx%nsRcYhHn3`_~IH%!W++k>B z$&tVS+I&#E7lL%dWH-TwKP+fsnsCc8F;f zcmBWwz&KRCvC!}r#Y$RDF&k&1l^?X{dxA93qXxs-=+$1&ILn0Dxr ztGZs^Y$_(KTFCjRb}dzh@JKuHMGbb&VRGu{`1V!5{{12Jt`(S@B^_nrnR7~+Xw_VYKOmbZgd2%d^Pc`8e_j^&+@XH@mG#pea8GuZWssZpbQI~G zH7QWS?8eRi+1Gy7% z^A?|cTb1x5;fGc3epW3iLN&TmZHv}w-HdcQPV1qoTiFyaJcg{3|NT|OttI%KlR4$3 zr?MNe&`tDLYAN5rF`Qc##W7A4E~)t-Ve9bueX6`iwsNQOW%Fj&)0rQD7}o zkV&U?tsv^TdXGLFg)#84b(-0qPt@Brrst+CQcO&ZPh}g_%LS588;sv7B*j~=5?wAS zNk-UI6t^OF_fDg=#cw|$HnU~VE!^S@HsF;!@~o*>e$(4LzT#%)G^Z9crRT+a!??wg zI&6hEKkBR}_@?XpE}7UvMLRQ?*flgXH~KyLnoao~?F-`1IEMYNvU1N=ru|&&N>ue1_x$}a zB)J0Y*YUL|p`1hD4p+E@J4Ssv7VWBR#oCn%uGOyf!Iv%Kku)_l#{`2G%{fpyT9llE zlob=mNekBpGqm)!k_Xq546O;rAXL(M8Le~ebYlZw2M`(5K1a_AQ_^LtK zcb-^4TWC3it$odT0)UqTIBv=ziafJLo+nZ+Hmf%!&UDwJb*=&|xOmk_wv4Sugu=x=fD zJi-FU8XFH~$702j-Fws`4|>O5p}48YNv@P;~mnYH%wOQ@nY?Y z{`z_-UZJp$;@=|hEUb0W+=Jp;sk*l1j1*J}@74(ZfJeX#RTFE$>}L!x^l+qsQAb9p zBRfWog&kY<pG@#qoQ7BJt#brc{@EBBys%$Q%D)lgvD()m#mXqp;X8G;=|}7D zX~19nD}TV&28f2?F9%LwLR=$EZk@0hV$>&d4Fz1r>WactC=GGr0G@&`ia!;@V)!UO z>msvfO9fZ?sQ@S?l8vihsHnr!#i#{fYwh?di5JVe+^D`Isy86RF~OnB#Mrsg_|5aO zFa6Bgkoek)#VmF zGKJ#gjbVKc$!a4pB~DuTb%<$UblVzPt;(oHO^DwCSFu7Wh@$<}#?oovyQ6lvweRM> zoO-q@UukSqGRr%;R&REMyl`+U@r*O`;?Z*R_@-IvsXG!AmpzO8we8sGSqx1V0D49L)^G;Ds%`qIz?O}Z z8k-61gwN*oIk5i9`EHnj#y5b{a}aKIKlDChn!8W&G)`t@^u)gYes;fhy)j%p+|wNI z*aFTOFX@=~mclJ~Sb`~Y^MXju5FAGO?gt~T?c!@3RdXQT1r!UT?c?#+nLNc`Y_-1v zg7w$@{Q_%~|FM9i8AAHlYH}$jCr3{FCme3rX@O&*h7)xj991;wmR#YvAzY5m6wyby zKXR3}AT~Z1fyRP_Z3Ml#f9ffiwSgG<&+4bUj3F!s5(5=A-&bQ%v}~{`_q%*_ql_{E zcz*x}>87ao6-KNilOv2MkiNz~rv?I_s&eaw7TAM}!b90{4LAPE*tdS{`>`iwF?*QBo z`k#CKriwo+q&I)RB}E5Yh7D;-oPetj9QA2(`$Zl4HP>u;jrZUy-$sXbRqtorEui7D zca{ubKX+xkg)Gruo$RYdkW;x^PVUdJQ5^VVOho#1JwsCm=_qB$5W;o9c{G7|usJ{& zh@m-$=uS&Kcakqqz_rSg;*|oNW#nj4RCRv2jEId)kUjlldj^^>SngcAV?#N70v1tZ zQ!TuAll~;&N2u-NjUH4aw^?X+$)l1v+GVOOMqulRY8D#p#(g&Cg1Y-7>nL@?^>58z#NN zO;3pqd_EimuONQ0#_brebB<8HY|mclT$U365%Xnk_1}SC9yiYRh4IMZi(#L~GR}D> z)4@0u#v4YBYCI0Ur7YA1AT7yj@{ZRh0Q+?DlCE4mq%g?3s< z->kH1^d?Sx)3@aO{pKqrm;b_FZwY0x!M`k7yz~D~d(p$GTo-yFK`r>zZMfMaug%FG z?wt>;6S`R}+*Cp#gj127nm?p|wkd6Vleue!P`8(>&uvHfSk-u9+J0y+Y~t@?j9#-j!J-d4?5fK5|>31;cIj zH2>_3(tIB;HDZbms(u-rfFIN0s-i?-tr;FIy|=@dzn1Xk5i@k73*Yu3aXpp1x}mz~ zXXDu81gIaleH_OOzZqk<3GRXSt>_8DzTGFZi|rWZEpF)kpd9bPktvh-Q0;6(#9(&~ zeVg3MUNA`F%cOZ=tfy02c)Wbd(TgQFQ&DrLbvb{RJ`a{YAC|W8D_P2>xEBZSqXp+2 zvKqe+PkXh5&i2+K+V6p`l&r24mzc4vP^^?VY%#OvVr;|3+}ek^u?Gu9RdBpQRY)GA z_E>2EkyGgFDw^_v$RKlODzV`BfjOZ|=um;TzZA`wS{L#TW_D*Fv#05h>Xo;@zp=j0 zvA+K_j~eK1PAo4&9wea>KWHtLC;k*P+?n3==|wuWlPAR0d*FtKmF zIH;g=vC=zh(alr(Ow1v^?&;O?93y?Xd(yNMzxXkaQ)JyuyqOWo%D0O*w$C#cGjE0c z1cEyg|KkX!D9M+VJafX?te46jWFhAq##aW)BmjHBR)O6tHo;qT=kvhT?)|wv+Ul8? zLxp*r?ZG3~Aa$LzBQ>pHxI6U)G#=lco?L3tdn0og?A}21C2?5*c5z+pIpdL>Q@rMz zY?OYQyz6OV5qH`Mwhww;A?i0J=@}4xr`0^dau2U@5ScMH*&~d&JBIXXL?`VWv0J{) z&(xJ;ayDbICu1moV+*n9l|LY}gWBH_^Ev==3hUWZ@{tQCbI7bCx3wr*Y(O+{u$hfn z1~JBVBdnHbCp^F~dvfPk4O8nDu7z`HjmjNv=CU^BXu-OQfA~~1Ve&1J{c>i0@5aIX zh8xAkUaROhgzL_MuEwQ`*xi(TFs5fSc+BIdO0x^hj;|eQCb}{r?)m}thboXF!uSGI zn~ugwnr!X7ky22$%pBL|Sx)+*LG zNxVH{xYgqHFZej(W@1|qSKPsfIP@9jK%1sL4_msUVC1-!uX^P8yjDGqraIiTF}H81 zjiW_syf>$^hJ49`efciUU8<`~gxchn?V4Ulf|mToaT?Q>y$1Sj)^?zUt$T;jBgfcG z!p9NI3p$Qh0A3G~cLV;a+O=Hga4sdyxiHrJ2_MU$#6q-nTl{Y7p%4VK-@v&`^5n-Q zXZPrHZC!5E#Qy%DlRcz@qc=Qf8u0il?YD$miP4oFV=KLeX1et|l23OZ3E2rxO&Er9 zRZ8dTsnNB_fXxzz53MT>c|l(<+_SZvQ8m%>+S;1po^I2irXb#ZFv2Su3aleo{(N`j zBMoFR#thI8^OeZYG3}`u_YDRrF!54Mf^0Az8#V2RL!_Me#@v)FKuC8&kVdt-D?p8P z#S$Vl7-Pypuz#*}epk!rP^A?{IAyavQHGN=LoF~z`qxSB3f_<33Q8)WSjq1uSNre7 zg=_tph6aZGt|~Ao2}I<~9&H1rXyM(`1bE?VrQk z!>w&`t%Ha2TRd-CUragPP{5_bSYOZ~AugwDWxdAZ)*zs8g2eUgK^%d)lK_~)8M2p(`}2Q;b}(J zM7eLslU5G)L(&Row-RUg)VSS9@E5%|DyZxMe(Y*jD7LZM`Av5ff&+tYXm<7fRZ!lf zqts;}Yjo%MtP;RG_ud@B5%zpp{7=5As?-`WSHb24;$q>>cGyYz?s}Z{6ymW}gnjbs8JB-^=H)~XS%*PSjXv`$l@&+uG511>plgO( z-bcqopz`cK`_3bLh11%|ZpInar^JWHYPeXjlgO3Tp~AKKrK|Z1_PO9RuVN$o_^5B| zXfG|XZ`BFk;30m>ajbBS4tgwDLJmPqnh&S07`l}J16q(-r#LrOO;$J4 zphosPcV$ne?7yuLCPD?t5N<6CE7Z3jqihTF$0S?s@=gD=B@HoYR|(I95l7nO*A+N8 zy?1|bjE+$HAf*3M*;zof(Y0$EZPB8^DN>x`t|{(r#UZ#;G`L%l;$E~kMGM6#A;616 zDa8wc0xeR4r-jm!Z=G`%Z#jLx|NAG&S~FQ|uFT#$^UTcN`?>EY_2E8EGl#wY@(Mc5 z=I0hi=NF%PdM_=``;PMRCf^2q-x6E*zL>~64e{Pl4+Tmi5P#`t%nk3o9g-gCrvN3- zB!tQ*#ghQ!nROJH;+@NYeBsp$_lTq72cU!?HohbZnIxtZ=9EXy2|CcHWhoR&NvwMb z*id;8F<)M;tl1MYWixs8N0N*h6HNT_+3*CKG***%V|h-#f?T-?<{hHb1hnRG$46wu zhfv0{sE)!fObzs1@k7uoC^3kAR^yCtJboLB17e<4t7kMMafz>sZ-dT5g`jOvU+6g$ zA1V%#YL4hgTU2&s6ecl^e+?A|Ni;_-s;tVds;|nfsva=m9WOhg zIx;#EI|@4DI&wN*bQFWb!KvVQa4t9+oC!_>7lI?f>EP$!d~hr{8=NAlEU87qXhCnm zWWo4=gomV&P$}LhVIh7Yp&UvLEr6OrSD+HmZfFqnDtyYQoQ{Z<7+Ei*eE45R-m+G{ ze34c@^4XV5df7fBce(FXU3cB3u6bP{6=p09tf;zBi}z?hh*EFCImx!gO3?OaQrelX zTdMBDdGO#{DWioSt>p|hRaBGkh0MIET$Mo)KCMXF$^w^#gq-@0o z)f@IcTq?A)THF@N+cVi#o}FB-+>b+c9Z)_(s-d`~Uec_NV3U36H16Nnq4}&!lwH5< zIZ5^?gU`gcFX`(pKlez>2;Z7wf5JI_orx3TsXA_CZ1pDo&^pzD5BK=&=$E&)XQsX_ zqv}&7n_?_MWWF1vm3v$YrbL|2tCC0?YSk5Ts>;SWbw_u6Y!*Sbi~w8y1|FK}eLcUT zsL1iHnw`S@c_C`Cy131Pv!tbo(Q^sc4n;>=ZPdt459TxA`ZfwFLb6X5B9MH9{P~@|w)oOLFzq*-f6OF#3U3AB0%q_mFrue$&%)fb3@ywF2;n zcqrwIzkGjPwpDkvaSZJ|6S$;3u;iy3lTC-pTwHJMS*at41E3*0?SDi-*lcgk<3+EjDks5f-znn5B+Nyf1Z zYF`Ec{4~!;4vl-ng{9QQos~q9?_ilt%_j0fM;_4u3(vA%LVs;~Nz*D1HPIn1B5&Q* zP))d|=q#zs*TM~RPf2o(=egnc7&uKW{JVmFd1TjCm1{I;(2U7)dY}dEN5{P=d)XOvynGcI0WrtQFUvE(>cpgzLqa}5hG-9ayM)Uc zU1pcRb$wX=)?Q(L*k!oVTGra2gl{s}+|=N<{a)O|SVCHcljO$_j-Rm{A?8_qrHVl& zK`n|`%Ejj8KWS(7v0gk?_91aOTblo2AHvz3yr_9V??)^`ByzbRvS$icWe(~xx@h;k z+*!%4->d#`JjZfwE_g3`{9lGC4 zEf=fq*Z(v|&?79P31Y+45QK=gF$6JT4QIVGjik~v(&h+0xOdEpa0!SKsIMOzNOofm z8yNsH_mC|Ra7&2jR_w-JbienOyE6IOSt_L$=4g)~0YgaR%K7yiy`I;u_S1;TfW=S6U@Y)!*I|$?qetdlj+2{QQf^BUa zLPiG3u^UIufke`%H@GkoSU;&W_KhAa0|q3K#=P-{k#B^mMO$LgFh-who^4}OF#!q;cmfmzJZL$vja zqX+wj7A8EbMI=pIL?n&A^^18m;@1u*oSsX%M;9eO$zIUXT&r{7Sjg{UCYB&B?Y|#d3lD;oN3X~A3z97BEt5F*#}|q1^>YhO1{tg`Qc2Vw0bd>2Q5eCv zV*wmJS=JRe9@bAQAZvIe9IJ!KndqQ(8KIhsJ99y0`1pZZH7a8SA|e#N z1uKPf$*5zlan!!`-l)uWyb``Z-~MZC4qlOqpuJe}3)6kE%e7Z8gtxcoE`vf>y{Dxv zSG|uyj=uV9C7~mgVyn|oBA(O7J<*^ibi{gt!5ES9M9WG$V%bo?DzdYk@hJP<$0sVH zgkRZ@qtTDSoj5z@Wp0{K*|dlHyXE3k`|JsRuGoGo?U)j(Xvcl&*|a|gymlJ1U@QQv zcE)%}PC3=}`G6#Mu*z~fWo9Fu8zv{k=*$7WR9 z^m*Plx%wkRJy=tGsd!+U z=!SQOxeY6Fv$LObj*Zb``msf48C3ffXUEz?avSmUR2?=8Kq97}RYJ@rHK?>toAFH+%ge{a{(*0I z*nC{qcQ`n(-*U|)pl zJ-P!6o9LeWA_qj6s6DYsE_y}2V=aE2g?KV! ztYe8p&SHrS02Ccr%~>aeWXbc+!echj6|n};9<8EvDq@iYXDSBMOeij5vphP86bnS4 z(R>_S!?0xP$k|ZCuw+5t41`9ad!}y4L^hHHM{N)eU=7G&tTQ3V2K=!eGJrGb?ZSgL zLwDRXn3N*ao!lJ?WCDH$o}< zJl-Wdq^Dq{U`7ZJ3}V<|tTT(sc&2XzVZ0!bj1WQNTzSGYONB*%0gI|$iz?2+Wgu`a zrO}6Thh#ENM%4kl9*kAs7$={2#XK%K(D{ohlasWCY}*CU65pc6u&;g7B>S=4tSl9u zts8LiIJ5bE>U8;utq*e*r|9dQH|cZpg$*ztKsigTreZTA<@vMDgjv}_d;-iVv)FV> zqinhL&wh!jTuB2fN?%-x@;*PysBry`^8JEO)Z;Bpl+nSIjpwkt)IEnodl*Gw#>kf% zuz)-J_Ge+tFhjbyFqTdgdYers&HIVOzo0?H58jAymd0t1e~UiuKeern}K=x>vtZRpnuk5O$K*Wt}A=inllh}0LvTPE_P z-=&Sk^orA$jvh-$jB=jGni%E=jxniA+J)dfs3)_ftI(Y#B;Ca}R6?zM-HFn<{Q@GE z*TYB|EAt^~qu(GXh?)9Yy@;1`)j+btg;x~yD>2@;$OXQX8)}*@*_`n}U~ZvdobUH% zh_KU=K#=Wbq<~ZpXRA;Qn8wjX-PP?uw|-_M5bN-{+X19D)oqudVsfVRu;7c3TQgDp z>ZZ(@;=lobBU)8rn-R0$-rd@g1(4VO1><{WzVwnE$O&gA`_ruR>MQdBlSIt0G zL5*2;{w23+t)6;CMMK_6g416?y!X4}m!5Go+E@_MUsx z6n`UO?7$KDr1PsBc-c|!!OTINvB(Nm=Q0Wp&}yLom)bmPe(|Gq-N?sqH}rPoyEJi!odryuGkOb0H5OTTg=!;%Lqdr6h;2GHrn)T7wJAzM}t zin89)up_8VzPXJ<6P3L2LRqtvhu9H%;(G-GIy{hSr-;)QJXO;xfsZi<)(~%{fusS; z9={Qa>nM$}g-%M>)%NF7x+t8TZc%zI6&r2I&N=gZp@duQ-2R(ZeoAJZ@3BMO%3lgj zC3VyMsE2(kl6MgFRZaU8RQ1ZAtNmOuOT@}$Vd5u%9HPbWG7?%n~HrL05#^k!`V59MRjcd zjgT;%Jh(}z?A70;iU`l>O$n?4#E`KD(Vf3%A^j+}gjt=QB9H$AYR!>4Q9sw8l1^3+Yq)AQgr(eXuza~vu=ss*L zPcLS5*GF3IOc_t~W02|2t8Nmyi|}=w&877{6SqR8dlq?w7$mEqIFe5MOdXs2JpYnE zD0r&LJo$CzoXhw5mTXI|eXgzxoy1qnghYy5RPw0QHQMEHKI++V2*csr^k7eG1l|VF^O2x3EP$=^Q_lr;HrGt{Q#07^^&}3sT-y1mey61U6ln zbzRltRy5LsmYW@jiQNbDFb0XZwjB9&H08@v*cen+dn$d5fE4)%)4qcuD-NU61;I93T3vRjNKkmS`)PtpiW*29Ou4N zGaJrG>VO(dB?paPNhUh4U7WC*d-Z*D%@wkD&rj*J7V12;aj&&n$rgA6tDa8(oMPN^ zVJ8g}*`yU-bn94WPIPxA5Ey^0G0uKWi8gE&+bW=0(Pq6gY2Dx`Zs6T(uOiAVljSK3 zQK96Il&h_~lZNieRRA0(h->0nR)s_t6AR`&eGfn~cX3J}h-aL;ZnX_QSuUmbKXG8{ftr z04kZxDQ3P=uHo zbc$L}zcx`BlC|?Gy8BY=cyYQV>F_TN;iIl^<#UFx;k@d5`G9lIjpp(_XK2^lRo0l- z;ioFv=%}ABv%u5~H4`t0SAMo!-nZ+2X5%6c2vG&F_RCjJf1<$(yX5e|AHQD9zF!8s z5}?|j#$qM(*`?T`#LQso*-j|ChF;6uC`TowhSj?eU|wc-Ha=~Y6IN?iZ5OhYkPF?l zy#7V$@{m@0{RNPZr5kB=vNA7Jp1A*Q$j_I#{Md*T-yAs($9kPA&7k3Eq=s}oqUw0F zj#zjzO=l}}xla706qRz_VN}%_;axa8pURT$)cBRi;0Ip$a7`L_Hrf~Z9qx5}E8WpI z?0X=clGvUM=$j7%*2^Uw#~4asL8Vvs(Ow8dvIvekVS+eb&al&*}44xX>j;0O*boD z1!+J>5mUVKp1B96`j5yJD#FiS-$%g(Fl(`yAVBY8rBT={Xy>{>33KYq~ zAi4SZ+Fwcg7iSNjH}->@Rt%YQh4p3Gnagii@kA4pTZJr-=Y-1F;#o!m3QtC!B|K}% zRIvezx2Kw~-*aUe%yl@9<>Rs8dzI$rUQ;5+l1J6=C~H(GkmmBVtwA-Q4E-X<9=}^z z_~5N+=(dmij%2~O$}R!TLIAd*%wbL~Kus>JG?4E>kh!G4x94|b)GAbDu-E(l3HE=_l+DuH*Cf#)EONu;@?}@+ zd}AVyns$iB#U|3pPN<=*EGSFw2$JMdGk^b0c!g`M{2iU83<-Avi*z`FU7=V|*BU-~ zh05;4RPee;PT}d*^b1Rr)F^Udq*h%ry5Cw!Z#G68rR)w;z(}@5hXM7!UcR zU9-y}I`)Q}suK3N{2;%rN{#-tT56w#VqjcYtf1lRy`6`lq;N|p@P}%~MgH_7nWK5t z#T&QEY^$^W4T*MGDtMxK)tQ*-feL7-eQEZi4&c7)ZiIp1+}hEDn_ffvw&hxbsx;!s zc-(~JcWPtI=9g+ZjpDI(UEc)>`dSWfIQ2f`w;C(#zVtc%(B-t(uEonxhPC9N(2Gri zIvZ~~p?7+DWjGR`5oX(JF@MN7pIVZk#MTYU*#oTkTsOPfQ`c*MES~|yd}BM4FExr4 z#u`ucOnxkPU;Zz7J8ibgC*622ZPPuKVo8U7 z|4^AB;EO1fi6Jb?_rnojP5r>=z#Db*rUm=}SK@PW(IBILW&nK(=G*Ai7|B_>lk>$MTKG>%Fq-{MjUQ;GZw_60M&jn>0>7xVUBtLdo;R_lc zN}~lVp`70cJqp1p`6gTU z9-k>Zj5>}LvkTJCcnA9?)j+4BgUuZ#G>7hu2W|v6NJV>8G*T2&WGuu&VMRJduEw2< zXCSv`ke`2j`}?AypfAIvqN&KEA)~3HsGz6Mt)Y0ApitN(+lSn(6&Yudwv&HbhLGzX zUJg7SAZI%pr2ig>$J*PCN52F$f zcTsmmnQl=6=6^!{L89p{@~-&HE%KrDKO_I`32|2f implements Iterable +{ + final T mapped; + final int elementCount; + + MappedForeach(T mapped, int elementCount) + { + this.mapped = mapped; + this.elementCount = elementCount; + } + + @Override + public Iterator iterator() + { + return new Iterator() + { + private int index = 0; + + @Override + public boolean hasNext() + { + return this.index < (MappedForeach.this.elementCount); + } + + @Override + public T next() + { + mapped.viewAddress = mapped.baseAddress + (this.index++) * mapped.stride; + + return mapped; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedHelper.java b/src/java/org/lwjgl/util/mapped/MappedHelper.java new file mode 100644 index 00000000..ab749f21 --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedHelper.java @@ -0,0 +1,181 @@ +/* + * Created on Jun 28, 2011 + */ + +package org.lwjgl.util.mapped; + +import java.nio.ByteBuffer; + +/** + * @author Riven + */ + +public class MappedHelper +{ + public static void setup(MappedObject mo, ByteBuffer buffer, int align, int sizeof) + { + if (mo.baseAddress != 0L) + throw new IllegalStateException("this method should not be called by user-code"); + + if (buffer == null) + throw new NullPointerException("buffer"); + if (!buffer.isDirect()) + throw new IllegalArgumentException("bytebuffer must be direct"); + mo.preventGC = buffer; + + if (align <= 0) + throw new IllegalArgumentException("invalid alignment"); + mo.align = align; + + if (sizeof % align != 0) + throw new IllegalStateException("sizeof not a multiple of alignment"); + mo.stride = sizeof; + + long addr = MappedObjectUnsafe.getBufferBaseAddress(buffer); + if (addr % align != 0) + throw new IllegalStateException("buffer address not aligned on " + align + " bytes"); + + mo.baseAddress = mo.viewAddress = addr; + } + + public static void put_views(MappedSet2 set, int view) + { + set.view(view); + } + + public static void put_views(MappedSet3 set, int view) + { + set.view(view); + } + + public static void put_views(MappedSet4 set, int view) + { + set.view(view); + } + + public static void put_view(MappedObject mapped, int view) + { + mapped.viewAddress = mapped.baseAddress + view * mapped.stride; + } + + public static int get_view(MappedObject mapped) + { + return (int) (mapped.viewAddress - mapped.baseAddress) / mapped.stride; + } + + public static MappedObject dup(MappedObject src, MappedObject dst) + { + dst.baseAddress = src.baseAddress; + dst.viewAddress = src.viewAddress; + dst.stride = src.stride; + dst.align = src.align; + dst.preventGC = src.preventGC; + return dst; + } + + public static MappedObject slice(MappedObject src, MappedObject dst) + { + dst.baseAddress = src.viewAddress; // ! + dst.viewAddress = src.viewAddress; + dst.stride = src.stride; + dst.align = src.align; + dst.preventGC = src.preventGC; + return dst; + } + + public static void copy(MappedObject src, MappedObject dst, int bytes) + { + MappedObjectUnsafe.INSTANCE.copyMemory(src.viewAddress, dst.viewAddress, bytes); + } + + public static ByteBuffer newBuffer(long address, int capacity) + { + return MappedObjectUnsafe.newBuffer(address, capacity); + } + + // ---- primitive fields read/write + + // byte + + public static void bput(byte value, long addr) + { + MappedObjectUnsafe.INSTANCE.putByte(addr, value); + } + + public static byte bget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getByte(addr); + } + + // short + + public static void sput(short value, long addr) + { + MappedObjectUnsafe.INSTANCE.putShort(addr, value); + } + + public static short sget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getShort(addr); + } + + // char + + public static void cput(char value, long addr) + { + MappedObjectUnsafe.INSTANCE.putChar(addr, value); + } + + public static char cget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getChar(addr); + } + + // int + + public static void iput(int value, long addr) + { + MappedObjectUnsafe.INSTANCE.putInt(addr, value); + } + + public static int iget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getInt(addr); + } + + // float + + public static void fput(float value, long addr) + { + MappedObjectUnsafe.INSTANCE.putFloat(addr, value); + } + + public static float fget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getFloat(addr); + } + + // long + + public static void jput(long value, long addr) + { + MappedObjectUnsafe.INSTANCE.putLong(addr, value); + } + + public static long jget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getLong(addr); + } + + // double + + public static void dput(double value, long addr) + { + MappedObjectUnsafe.INSTANCE.putDouble(addr, value); + } + + public static double dget(long addr) + { + return MappedObjectUnsafe.INSTANCE.getDouble(addr); + } +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedObject.java b/src/java/org/lwjgl/util/mapped/MappedObject.java new file mode 100644 index 00000000..2d05ce5b --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedObject.java @@ -0,0 +1,185 @@ +/* + * Created on Jun 25, 2011 + */ + +package org.lwjgl.util.mapped; + +import java.nio.ByteBuffer; + +/** + * @author Riven + */ + +public class MappedObject +{ + public MappedObject() + { + // + } + + // these fields are not assignable/writable by user-code + public long baseAddress; + public long viewAddress; + public int stride; + public int align; + + /** + * Holds the value of sizeof of the sub-type of this MappedObject
+ *
+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.SIZEOF will yield 8 (2 floats)
+ * Vec3.SIZEOF will yield 12 (3 floats)
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + + // any method that calls these field will have its call-site modified + public static int SIZEOF = -1; // 'final' per subtype + public int view; // read/write + + public final void next() + { + this.viewAddress += this.stride; + } + + /** + * Creates a MappedObject instance, mapping the memory region of the specified direct ByteBuffer.
+ *
+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.map(buffer) will return a mapped Vec2 instance.
+ * Vec3.map(buffer) will return a mapped Vec3 instance.
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + + @SuppressWarnings("unused") + public static T map(ByteBuffer bb) + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + @SuppressWarnings("unused") + public static T map(long address, int capacity) + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + /** + * Creates a MappedObject instance, mapping the memory region of an allocated direct ByteBuffer with a capacity of elementCount*SIZEOF + *
+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.malloc(int) will return a mapped Vec2 instance.
+ * Vec3.malloc(int) will return a mapped Vec3 instance.
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + + @SuppressWarnings("unused") + public static T malloc(int elementCount) + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + /** + * Creates an identical new MappedObject instance, comparable to the + * contract of ByteBuffer.duplicate() + */ + + public final T dup() + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + /** + * Creates a new MappedObject instance, with a base offset equal to + * the offset of the current view, comparable to the contract of ByteBuffer.slice() + */ + + public final T slice() + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + public final void runViewConstructor() + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + /** + * Copies and amount of SIZEOF bytes, from the current + * mapped object, to the specified mapped object. + */ + + @SuppressWarnings("unused") + public final void copyTo(T target) + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + /** + * Copies and amount of SIZEOF*instances bytes, from the + * current mapped object, to the specified mapped object. + */ + + @SuppressWarnings("unused") + public final void copyRange(T target, int instances) + { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + + /** + * Creates an Iterable that will step through + * elementCount views, leaving the view at + * the last valid value.
+ *
+ * For convenience you are encouraged to static-import this specific method: + * import static org.lwjgl.util.mapped.MappedObject.foreach; + */ + + public static MappedForeach foreach(T mapped, int elementCount) + { + return new MappedForeach(mapped, elementCount); + } + + /** + * Configures a newly initiated mapped object with the specified stride and offset. + * @throws IllegalStateException if view is not at index 0 + */ + + public static final T configure(T mapped, int stride, int offset) + { + if (mapped.baseAddress != mapped.viewAddress) + throw new IllegalStateException("view must be zero"); + + if (offset < 0) + throw new IllegalStateException("offset must not be negative: " + offset); + if (offset % mapped.align != 0) + throw new IllegalStateException("offset not a multiple of alignment: " + offset); + + if (stride < mapped.stride) + throw new IllegalStateException("new stride must not be smaller than current stride: " + stride); + if (stride % mapped.align != 0) + throw new IllegalStateException("stride not a multiple of alignment: " + stride); + + mapped.baseAddress += offset; + mapped.viewAddress += offset; + mapped.stride = stride; + + return mapped; + } + + ByteBuffer preventGC; + + public ByteBuffer backingByteBuffer() + { + return this.preventGC; + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java b/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java new file mode 100644 index 00000000..a8fd803e --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java @@ -0,0 +1,136 @@ +/* + * Created on Jun 24, 2011 + */ + +package org.lwjgl.util.mapped; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URLClassLoader; +import java.util.Arrays; + +/** + * @author Riven + */ + +public class MappedObjectClassLoader extends URLClassLoader +{ + static final String MAPPEDOBJECT_PACKAGE_PREFIX; + + static + { + MAPPEDOBJECT_PACKAGE_PREFIX = MappedObjectClassLoader.class.getPackage().getName() + "."; + } + + static boolean FORKED = false; + + public static boolean fork(Class< ? > mainClass, String[] args) + { + if (FORKED) + { + return false; + } + + FORKED = true; + + try + { + URLClassLoader loader = new MappedObjectClassLoader(mainClass); + Class< ? > replacedMainClass = loader.loadClass(mainClass.getName()); + Method mainMethod = replacedMainClass.getMethod("main", String[].class); + mainMethod.invoke(null, new Object[] { args }); + } + catch (InvocationTargetException exc) + { + Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), exc.getCause()); + } + catch (Throwable cause) + { + throw new Error("failed to fork", cause); + } + + return true; + } + + private MappedObjectClassLoader(Class< ? > mainClass) + { + super(((URLClassLoader) mainClass.getClassLoader()).getURLs()); + } + + private static long total_time_transforming; + + @Override + protected synchronized Class< ? > loadClass(String name, boolean resolve) throws ClassNotFoundException + { + if (name.startsWith("java.")) + return super.loadClass(name, resolve); + if (name.startsWith("javax.")) + return super.loadClass(name, resolve); + + if (name.startsWith("sun.")) + return super.loadClass(name, resolve); + if (name.startsWith("sunw.")) + return super.loadClass(name, resolve); + + // never transform classes in this very package, sub-packages should be transformed + if (name.startsWith(MAPPEDOBJECT_PACKAGE_PREFIX)) + if (name.substring(MAPPEDOBJECT_PACKAGE_PREFIX.length()).indexOf('.') == -1) + return super.loadClass(name, resolve); + + String className = name.replace('.', '/'); + + if (MappedObjectTransformer.PRINT_ACTIVITY) + System.out.println(MappedObjectClassLoader.class.getSimpleName() + ": " + className); + + byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class"))); + + long t0 = System.nanoTime(); + bytecode = MappedObjectTransformer.transformFieldAccess(className, bytecode); + long t1 = System.nanoTime(); + total_time_transforming += (t1 - t0); + + if (MappedObjectTransformer.PRINT_TIMING) + System.out.println("transforming " + className + " took " + (t1 - t0) / 1000 + " micros (total: " + (total_time_transforming / 1000 / 1000) + "ms)"); + + Class< ? > clazz = super.defineClass(name, bytecode, 0, bytecode.length); + if (resolve) + resolveClass(clazz); + return clazz; + } + + static byte[] readStream(InputStream in) + { + byte[] bytecode = new byte[256]; + int len = 0; + try + { + while (true) + { + if (bytecode.length == len) + bytecode = Arrays.copyOf(bytecode, len * 2); + int got = in.read(bytecode, len, bytecode.length - len); + if (got == -1) + break; + len += got; + } + } + catch (IOException exc) + { + // stop! + } + finally + { + try + { + in.close(); + } + catch (IOException exc) + { + // ignore... + } + } + return Arrays.copyOf(bytecode, len); + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java b/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java new file mode 100644 index 00000000..9ff1f0cb --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java @@ -0,0 +1,588 @@ +/* + * Created on Jun 23, 2011 + */ + +package org.lwjgl.util.mapped; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import org.objectweb.asm.*; +import org.objectweb.asm.util.TraceClassVisitor; +import static org.objectweb.asm.Opcodes.*; + +/** + * @author Riven + */ + +public class MappedObjectTransformer +{ + static final boolean PRINT_TIMING = true; + static final boolean PRINT_ACTIVITY = false; + static final boolean PRINT_BYTECODE = false; + static final Map className_to_subtype; + + static + { + className_to_subtype = new HashMap(); + + { + // HACK: required for mapped.view++ + // + // because the compiler generates: + // => GETFIELD MappedObject.view + // => ICONST_1 + // => IADD + // => PUTFIELD MyMappedType.view + // + // instead of: + // => GETFIELD MyMappedType.view + // => ICONST_1 + // => IADD + // => PUTFIELD MyMappedType.view + // + MappedSubtypeInfo info = new MappedSubtypeInfo(jvmClassName(MappedObject.class), -1, -1); + className_to_subtype.put(info.className, info); + } + + String vmName = System.getProperty("java.vm.name"); + if (vmName != null && !vmName.contains("Server")) + { + System.err.println("Warning: " + MappedObject.class.getSimpleName() + "s have inferiour performance on Client VMs, please consider switching to a Server VM."); + } + } + + public static void register(Class< ? > type) + { + if (MappedObjectClassLoader.FORKED) + return; + + MappedType mapped = type.getAnnotation(MappedType.class); + if (mapped == null) + throw new InternalError("missing " + MappedType.class.getName() + " annotation"); + + String className = jvmClassName(type); + + MappedSubtypeInfo mappedType = new MappedSubtypeInfo(className, mapped.sizeof(), mapped.align()); + + int advancingOffset = 0; + + for (Field field : type.getDeclaredFields()) + { + // static fields are never mapped + if ((field.getModifiers() & Modifier.STATIC) != 0) + continue; + + // we only support primitives and ByteBuffers + if (!field.getType().isPrimitive() && field.getType() != ByteBuffer.class) + throw new InternalError("field '" + className + "." + field.getName() + "' not supported: " + field.getType()); + + MappedField meta = field.getAnnotation(MappedField.class); + if (meta == null && !mapped.autoGenerateOffsets()) + throw new InternalError("field '" + className + "." + field.getName() + "' missing annotation " + MappedField.class.getName() + ": " + className); + + // quick hack + long byteOffset = meta == null ? advancingOffset : meta.byteOffset(); + long byteLength; + if (field.getType() == long.class || field.getType() == double.class) + byteLength = 8; + else if (field.getType() == int.class || field.getType() == float.class) + byteLength = 4; + else if (field.getType() == char.class || field.getType() == short.class) + byteLength = 2; + else if (field.getType() == byte.class) + byteLength = 1; + else if (field.getType() == ByteBuffer.class) + byteLength = meta.byteLength(); + else + throw new IllegalStateException(field.getType().getName()); + + if (field.getType() != ByteBuffer.class) + if ((advancingOffset % byteLength) != 0) + throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName()); + + if (PRINT_ACTIVITY) + System.out.println(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]"); + + mappedType.fieldToOffset.put(field.getName(), Long.valueOf(byteOffset)); + mappedType.fieldToLength.put(field.getName(), Long.valueOf(byteLength)); + + advancingOffset += byteLength; + } + + if (className_to_subtype.put(className, mappedType) != null) + { + throw new InternalError("duplicate mapped type: " + className); + } + } + + public static final String view_constructor_method = "_construct_view_"; + + public static byte[] transformFieldAccess(final String className, byte[] bytecode) + { + int flags = ClassWriter.COMPUTE_FRAMES; + + ClassWriter writer = new ClassWriter(flags); + + ClassAdapter adapter = new ClassAdapter(writer) + { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) + { + { + MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className); + + if (mappedSubtype != null && !mappedSubtype.className.equals(jvmClassName(MappedObject.class))) + { + if (name.equals("")) + { + if (!desc.equals("()V")) + throw new IllegalStateException(className + " can only have a default constructor, found: " + desc); + + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, jvmClassName(MappedObject.class), "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + + // put the method body in another method + name = view_constructor_method; + } + } + } + + return new MappedInstanceMethodAdapter(className, name, desc, super.visitMethod(access, name, desc, signature, exceptions)); + } + + @Override + public FieldVisitor visitField(int access, String fieldName, String typeName, String dunno, Object value) + { + // remove redirected fields + MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className); + + if (mappedSubtype != null && mappedSubtype.fieldToOffset.containsKey(fieldName)) + { + if (PRINT_ACTIVITY) + System.out.println(MappedObjectTransformer.class.getSimpleName() + ": discarding field: " + className + "." + fieldName + ":" + typeName); + return null; + } + + return super.visitField(access, fieldName, typeName, dunno, value); + } + }; + + new ClassReader(bytecode).accept(adapter, 0); + bytecode = writer.toByteArray(); + + if (PRINT_BYTECODE) + printBytecode(bytecode, adapter); + + return bytecode; + } + + static void printBytecode(byte[] bytecode, ClassAdapter adapter) + { + StringWriter sw = new StringWriter(); + ClassVisitor tracer = new TraceClassVisitor(adapter, new PrintWriter(sw)); + new ClassReader(bytecode).accept(tracer, 0); + String dump = sw.toString(); + + System.out.println(dump); + } + + static void throwException0(MethodVisitor visitor, Class< ? > type, String msg) + { + String errorType = jvmClassName(type); + + visitor.visitTypeInsn(NEW, errorType); + visitor.visitInsn(DUP); + visitor.visitLdcInsn(msg); + visitor.visitMethodInsn(INVOKESPECIAL, errorType, "", "(Ljava/lang/String;)V"); + visitor.visitInsn(ATHROW); + } + + static class MappedInstanceMethodAdapter extends MethodAdapter + { + private final String className; + private final String methodName; + private final String descr; + + public MappedInstanceMethodAdapter(String className, String methodName, String descr, MethodVisitor backing) + { + super(backing); + this.className = className; + this.methodName = methodName; + this.descr = descr; + } + + @Override + public void visitTypeInsn(int opcode, String typeName) + { + if (opcode == NEW && className_to_subtype.containsKey(typeName)) + { + throw new IllegalAccessError("must not manually create instances of " + typeName + " in method " + this.className + "." + this.methodName + this.descr); + } + + super.visitTypeInsn(opcode, typeName); + } + + @Override + public void visitMethodInsn(int opcode, String className, String methodName, String signature) + { + if (opcode == INVOKESPECIAL && className.equals(jvmClassName(MappedObject.class)) && methodName.equals("") && signature.equals("()V")) + { + // stack: instance + visitInsn(POP); + // stack: - + return; + } + + for (MappedSubtypeInfo mappedType : className_to_subtype.values()) + { + boolean isMapDirectMethod = (opcode == INVOKESTATIC && methodName.equals("map") && className.equals(mappedType.className) && signature.equals("(JI)L" + jvmClassName(MappedObject.class) + ";")); + boolean isMapBufferMethod = (opcode == INVOKESTATIC && methodName.equals("map") && className.equals(mappedType.className) && signature.equals("(Ljava/nio/ByteBuffer;)L" + jvmClassName(MappedObject.class) + ";")); + boolean isMallocMethod = (opcode == INVOKESTATIC && methodName.equals("malloc") && className.equals(mappedType.className) && signature.equals("(I)L" + jvmClassName(MappedObject.class) + ";")); + + if ((isMapDirectMethod || isMapBufferMethod) || isMallocMethod) + { + if (isMallocMethod) + { + // stack: count + pushInt(super.mv, mappedType.sizeof); + // stack: sizeof, count + super.visitInsn(IMUL); + // stack: bytes + super.visitMethodInsn(INVOKESTATIC, jvmClassName(ByteBuffer.class), "allocateDirect", "(I)L" + jvmClassName(ByteBuffer.class) + ";"); + // stack: buffer + } + else if (isMapDirectMethod) + { + // stack: capacity, address + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"); + // stack: buffer + } + + // stack: buffer + super.visitTypeInsn(NEW, className); + // stack: new, buffer + super.visitInsn(DUP); + // stack: new, new, buffer + super.visitMethodInsn(INVOKESPECIAL, className, "", "()V"); + // stack: new, buffer + super.visitInsn(DUP_X1); + // stack: new, buffer, new + super.visitInsn(SWAP); + // stack: buffer, new, new + pushInt(super.mv, mappedType.align); + // stack: int, buffer, new, new + pushInt(super.mv, mappedType.sizeof); + // stack: int, int, buffer, new, new + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "setup", "(L" + jvmClassName(MappedObject.class) + ";Ljava/nio/ByteBuffer;II)V"); + // stack: new + return; + } + + if (opcode == INVOKEVIRTUAL && methodName.equals("dup") && className.equals(mappedType.className) && signature.equals("()L" + jvmClassName(MappedObject.class) + ";")) + { + // stack: this + super.visitTypeInsn(NEW, className); + // stack: new, this + super.visitInsn(DUP); + // stack: new, new, this + super.visitMethodInsn(INVOKESPECIAL, className, "", "()V"); + // stack: new, this + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "dup", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";)L" + jvmClassName(MappedObject.class) + ";"); + // stack: new + return; + } + + if (opcode == INVOKEVIRTUAL && methodName.equals("slice") && className.equals(mappedType.className) && signature.equals("()L" + jvmClassName(MappedObject.class) + ";")) + { + // stack: this + super.visitTypeInsn(NEW, className); + // stack: new, this + super.visitInsn(DUP); + // stack: new, new, this + super.visitMethodInsn(INVOKESPECIAL, className, "", "()V"); + // stack: new, this + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "slice", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";)L" + jvmClassName(MappedObject.class) + ";"); + // stack: new + return; + } + + // + + if (opcode == INVOKEVIRTUAL && methodName.equals("runViewConstructor") && className.equals(mappedType.className) && signature.equals("()V")) + { + // stack: this + super.visitInsn(DUP); + // stack: this, this + super.visitMethodInsn(INVOKEVIRTUAL, className, view_constructor_method, "()V"); + // stack: this + return; + } + + // + + if (opcode == INVOKEVIRTUAL && methodName.equals("copyTo") && className.equals(mappedType.className) && signature.equals("(L" + jvmClassName(MappedObject.class) + ";)V")) + { + // stack: target, this + pushInt(super.mv, mappedType.sizeof); + // stack: sizeof, target, this + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "copy", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";I)V"); + // stack: - + return; + } + + if (opcode == INVOKEVIRTUAL && methodName.equals("copyRange") && className.equals(mappedType.className) && signature.equals("(L" + jvmClassName(MappedObject.class) + ";I)V")) + { + // stack: instances, target, this + pushInt(super.mv, mappedType.sizeof); + // stack: sizeof, instances, target, this + super.visitInsn(IMUL); + // stack: bytes, target, this + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "copy", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";I)V"); + // stack: - + return; + } + } + + super.visitMethodInsn(opcode, className, methodName, signature); + } + + static void throwAccessErrorOnReadOnlyField(String className, String fieldName) + { + throw new IllegalAccessError("field '" + className + "." + fieldName + "' is final"); + } + + @Override + public void visitFieldInsn(int opcode, String className, String fieldName, String typeName) + { + MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className); + if (mappedSubtype == null) + { + String mappedSetPrefix = jvmClassName(MappedSet.class); + + outer: if (fieldName.equals("view") && className.startsWith(mappedSetPrefix)) + { + if (opcode == GETFIELD) + throwAccessErrorOnReadOnlyField(className, fieldName); + if (opcode != PUTFIELD) + break outer; + + // stack: index, this + if (false) + break outer; + else if (className.equals(jvmClassName(MappedSet2.class))) + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_views", "(L" + jvmClassName(MappedSet2.class) + ";I)V"); + else if (className.equals(jvmClassName(MappedSet3.class))) + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_views", "(L" + jvmClassName(MappedSet3.class) + ";I)V"); + else if (className.equals(jvmClassName(MappedSet4.class))) + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_views", "(L" + jvmClassName(MappedSet4.class) + ";I)V"); + else + break outer; + // stack: - + return; + } + + // early out + super.visitFieldInsn(opcode, className, fieldName, typeName); + return; + } + + if (fieldName.equals("SIZEOF")) + { + if (!typeName.equals("I")) + throw new IllegalStateException(); + + if (opcode == GETSTATIC) + { + pushInt(super.mv, mappedSubtype.sizeof); + return; + } + if (opcode == PUTSTATIC) + { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + } + + if (fieldName.equals("view")) + { + if (!typeName.equals("I")) + throw new IllegalStateException(); + + if (opcode == GETFIELD) + { + // stack: instance + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "get_view", "(L" + jvmClassName(MappedObject.class) + ";)I"); + return; + } + if (opcode == PUTFIELD) + { + // stack: int, instance + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_view", "(L" + jvmClassName(MappedObject.class) + ";I)V"); + return; + } + } + + if (fieldName.equals("align")) + { + if (!typeName.equals("I")) + throw new IllegalStateException(); + + if (opcode == GETFIELD) + { + // stack: instance + super.visitInsn(POP); + // stack: - + pushInt(super.mv, mappedSubtype.align); + // stack: int + return; + } + if (opcode == PUTFIELD) + { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + } + + if (fieldName.equals("stride")) + { + if (!typeName.equals("I")) + throw new IllegalStateException(); + + if (opcode == GETFIELD) + { + // do not change a thing + } + if (opcode == PUTFIELD) + { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + } + + if (fieldName.equals("baseAddress") || fieldName.equals("viewAddress")) + { + if (!typeName.equals("J")) + throw new IllegalStateException(); + + if (opcode == GETFIELD) + { + // do not change a thing + } + if (opcode == PUTFIELD) + { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + } + + Long fieldOffset = mappedSubtype.fieldToOffset.get(fieldName); + if (fieldOffset == null) + { + // early out + super.visitFieldInsn(opcode, className, fieldName, typeName); + return; + } + + if (typeName.equals("L" + jvmClassName(ByteBuffer.class) + ";")) + { + if (opcode == PUTFIELD) + { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + if (opcode == GETFIELD) + { + Long fieldLength = mappedSubtype.fieldToLength.get(fieldName); + + super.visitFieldInsn(GETFIELD, mappedSubtype.className, "viewAddress", "J"); + super.visitLdcInsn(fieldOffset); + super.visitInsn(LADD); + super.visitLdcInsn(fieldLength); + super.visitInsn(L2I); + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"); + + return; + } + } + + if (opcode == PUTFIELD) + { + super.visitInsn(SWAP); + super.visitFieldInsn(GETFIELD, mappedSubtype.className, "viewAddress", "J"); + super.visitLdcInsn(fieldOffset); + super.visitInsn(LADD); + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), typeName.toLowerCase() + "put", "(" + typeName + "J)V"); + return; + } + if (opcode == GETFIELD) + { + super.visitFieldInsn(GETFIELD, mappedSubtype.className, "viewAddress", "J"); + super.visitLdcInsn(fieldOffset); + super.visitInsn(LADD); + super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), typeName.toLowerCase() + "get", "(J)" + typeName); + return; + } + + // original field access + super.visitFieldInsn(opcode, className, fieldName, typeName); + return; + } + } + + static void pushInt(MethodVisitor mv, int value) + { + if (value == -1) + mv.visitInsn(ICONST_M1); + else if (value == 0) + mv.visitInsn(ICONST_0); + else if (value == 1) + mv.visitInsn(ICONST_1); + else if (value == 2) + mv.visitInsn(ICONST_2); + else if (value == 3) + mv.visitInsn(ICONST_3); + else if (value == 4) + mv.visitInsn(ICONST_4); + else if (value == 5) + mv.visitInsn(ICONST_5); + else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) + mv.visitIntInsn(BIPUSH, value); + else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) + mv.visitIntInsn(SIPUSH, value); + else + mv.visitLdcInsn(Integer.valueOf(value)); + } + + static String jvmClassName(Class< ? > type) + { + return type.getName().replace('.', '/'); + } + + static class MappedSubtypeInfo + { + public final String className; + + public int sizeof; + public int align; + + public Map fieldToOffset; + public Map fieldToLength; + + public MappedSubtypeInfo(String className, int sizeof, int align) + { + this.className = className; + + this.sizeof = sizeof; + this.align = align; + + this.fieldToOffset = new HashMap(); + this.fieldToLength = new HashMap(); + } + } +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java b/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java new file mode 100644 index 00000000..3cb4cca1 --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java @@ -0,0 +1,83 @@ +/* + * Created on Jun 24, 2011 + */ + +package org.lwjgl.util.mapped; + +import java.lang.reflect.Field; +import java.nio.Buffer; +import java.nio.ByteBuffer; + +import sun.misc.Unsafe; + +/** + * @author Riven + */ + +public class MappedObjectUnsafe +{ + public static final Unsafe INSTANCE = getUnsafeInstance(); + public static final int ADDRESS_SIZE = INSTANCE.addressSize(); + private static final Object[] ARRAY = new Object[1]; + private static final long ARRAY_ELEMENT_OFFSET = INSTANCE.arrayBaseOffset(ARRAY.getClass()); + + private static final long BUFFER_ADDRESS_OFFSET = getObjectFieldOffset(ByteBuffer.class, "address"); + private static final long BUFFER_CAPACITY_OFFSET = getObjectFieldOffset(ByteBuffer.class, "capacity"); + + // + + public static long getBufferBaseAddress(Buffer buffer) + { + return INSTANCE.getLong(buffer, BUFFER_ADDRESS_OFFSET); + } + + private static final ByteBuffer global = ByteBuffer.allocateDirect(4 * 1024); + + public static ByteBuffer newBuffer(long address, int capacity) + { + if (address <= 0L || capacity < 0) + throw new IllegalStateException("you almost crashed the jvm"); + + ByteBuffer buffer = global.duplicate(); + INSTANCE.putLong(buffer, BUFFER_ADDRESS_OFFSET, address); + INSTANCE.putInt(buffer, BUFFER_CAPACITY_OFFSET, capacity); + buffer.position(0); + buffer.limit(capacity); + return buffer; + } + + + + private static long getObjectFieldOffset(Class< ? > type, String fieldName) + { + while (type != null) + { + try + { + return INSTANCE.objectFieldOffset(type.getDeclaredField(fieldName)); + } + catch (Throwable t) + { + type = type.getSuperclass(); + } + } + throw new InternalError(); + } + + private static Unsafe getUnsafeInstance() + { + try + { + ByteBuffer buffer = ByteBuffer.allocateDirect(1); + Field unsafeField = buffer.getClass().getDeclaredField("unsafe"); + unsafeField.setAccessible(true); + Unsafe instance = (Unsafe) unsafeField.get(buffer); + buffer.flip(); // prevented 'buffer' from being gc'ed + return instance; + } + catch (Exception exc) + { + throw new InternalError(); + } + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedSet.java b/src/java/org/lwjgl/util/mapped/MappedSet.java new file mode 100644 index 00000000..4210f74e --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedSet.java @@ -0,0 +1,23 @@ +/* + * Created on Jul 11, 2011 + */ + +package org.lwjgl.util.mapped; + +public class MappedSet +{ + public static MappedSet2 create(MappedObject a, MappedObject b) + { + return new MappedSet2(a, b); + } + + public static MappedSet3 create(MappedObject a, MappedObject b, MappedObject c) + { + return new MappedSet3(a, b, c); + } + + public static MappedSet4 create(MappedObject a, MappedObject b, MappedObject c, MappedObject d) + { + return new MappedSet4(a, b, c, d); + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedSet2.java b/src/java/org/lwjgl/util/mapped/MappedSet2.java new file mode 100644 index 00000000..2a15080a --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedSet2.java @@ -0,0 +1,30 @@ +/* + * Created on Jul 11, 2011 + */ + +package org.lwjgl.util.mapped; + +public class MappedSet2 +{ + private final MappedObject a, b; + + MappedSet2(MappedObject a, MappedObject b) + { + this.a = a; + this.b = b; + } + + public int view; + + void view(int view) + { + this.a.viewAddress = this.a.baseAddress + this.a.stride * view; + this.b.viewAddress = this.b.baseAddress + this.b.stride * view; + } + + public void next() + { + this.a.viewAddress += this.a.stride; + this.b.viewAddress += this.b.stride; + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedSet3.java b/src/java/org/lwjgl/util/mapped/MappedSet3.java new file mode 100644 index 00000000..6adac0cc --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedSet3.java @@ -0,0 +1,33 @@ +/* + * Created on Jul 11, 2011 + */ + +package org.lwjgl.util.mapped; + +public class MappedSet3 +{ + private final MappedObject a, b, c; + + MappedSet3(MappedObject a, MappedObject b, MappedObject c) + { + this.a = a; + this.b = b; + this.c = c; + } + + public int view; + + void view(int view) + { + this.a.viewAddress = this.a.baseAddress + this.a.stride * view; + this.b.viewAddress = this.b.baseAddress + this.b.stride * view; + this.c.viewAddress = this.c.baseAddress + this.c.stride * view; + } + + public void next() + { + this.a.viewAddress += this.a.stride; + this.b.viewAddress += this.b.stride; + this.c.viewAddress += this.c.stride; + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedSet4.java b/src/java/org/lwjgl/util/mapped/MappedSet4.java new file mode 100644 index 00000000..a8a98eab --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedSet4.java @@ -0,0 +1,36 @@ +/* + * Created on Jul 11, 2011 + */ + +package org.lwjgl.util.mapped; + +public class MappedSet4 +{ + private final MappedObject a, b, c, d; + + MappedSet4(MappedObject a, MappedObject b, MappedObject c, MappedObject d) + { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + public int view; + + void view(int view) + { + this.a.viewAddress = this.a.baseAddress + this.a.stride * view; + this.b.viewAddress = this.b.baseAddress + this.b.stride * view; + this.c.viewAddress = this.c.baseAddress + this.c.stride * view; + this.d.viewAddress = this.d.baseAddress + this.d.stride * view; + } + + public void next() + { + this.a.viewAddress += this.a.stride; + this.b.viewAddress += this.b.stride; + this.c.viewAddress += this.c.stride; + this.d.viewAddress += this.d.stride; + } +} diff --git a/src/java/org/lwjgl/util/mapped/MappedType.java b/src/java/org/lwjgl/util/mapped/MappedType.java new file mode 100644 index 00000000..e655d066 --- /dev/null +++ b/src/java/org/lwjgl/util/mapped/MappedType.java @@ -0,0 +1,21 @@ +/* + * Created on Jun 24, 2011 + */ + +package org.lwjgl.util.mapped; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author Riven + */ + +@Retention(RetentionPolicy.RUNTIME) +public @interface MappedType { + int sizeof(); + + int align() default 4; + + boolean autoGenerateOffsets() default true; +}