packages feed

imj-measure-stdout (empty) → 0.1.0.2

raw patch · 7 files changed

+313/−0 lines, 7 filesdep +basedep +imj-preludedep +optparse-applicativesetup-changed

Dependencies added: base, imj-prelude, optparse-applicative

Files

+ CHANGELOG.md view
@@ -0,0 +1,8 @@+# Changelog++This project adheres to [Haskell PVP](https://pvp.haskell.org/).+++## 0.1.0.2 [2018-01-01]++- Initial release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Olivier Sohn (c) 2017 - 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Olivier Sohn nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+# What is it?++A terminal program I wrote to visually determine the maximal size of stdout.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,36 @@+module Main where++import           Imj.Prelude++import           Options.Applicative++import           Imj.Measure.Stdout+++otherTestsParser :: Parser Settings+otherTestsParser = OtherTests+      <$> switch+          ( long "modes"+          <> short 'm'+          <> help "Test various buffering modes")++testSizesParser :: Parser Settings+testSizesParser = LengthTest+      <$> option auto+          ( long "size"+         <> short 's'+         <> help ("Tests the maximum capacity of stdout. The size value is expected to be >= 2. "+                  ++ "Looking at your terminal's output, when "+                  ++ "you see 1, then 2, then 3, it means : size > stdout maximum size. "+                  ++ "Else if you see 1, then '2 and 3 at the same time': size < stdout maximum size. "+                 )+         <> metavar "INT" )++main :: IO ()+main =+  testStdout =<< customExecParser p opts+  where+    opts = info ((testSizesParser <|> otherTestsParser) <**> helper)+      ( fullDesc+     <> progDesc "imj-measure-stdout-exe can measure the size of stdout." )+    p = prefs showHelpOnError
+ imj-measure-stdout.cabal view
@@ -0,0 +1,46 @@+name:                imj-measure-stdout+version:             0.1.0.2+Category:            Console, Terminal+Synopsis:            An application to determine the maximum capacity of stdout buffer.+Description:         The program helps you determine the maximum capacity of your stdout buffer.+                     .++                     It was initially developped to diagnose screen tearing issues+                     in a terminal-based game I was developping, and more specifically, to see the+                     effect of the following action :+                     .++                     > hSetBuffering stdout $ BlockBuffering $ Just maxBound+                     .++                     Using this program, I found that, on my system, the previous call quadruples+                     stdout's capacity, from 2048 to 8192 bytes.+homepage:            https://github.com/OlivierSohn/hamazed/blob/master/imj-measure-stdout/README.md+bug-reports:         https://github.com/OlivierSohn/hamazed/issues/+license:             BSD3+license-file:        LICENSE+author:              Olivier Sohn+maintainer:          olivier.sohn@gmail.com+copyright:           2017 - 2018 Olivier Sohn+build-type:          Simple+extra-source-files:  README.md CHANGELOG.md+cabal-version:       >=1.10++Tested-With: GHC == 8.0.2, GHC == 8.2.2++executable imj-measure-stdout-exe+  hs-source-dirs:      app, src+  main-is:             Main.hs+  other-modules:       Imj.Measure.Stdout+  ghc-options:       -Wall -fpedantic-bottoms -Wredundant-constraints+                     -threaded -rtsopts -with-rtsopts=-maxN4+                     -fexcess-precision -optc-ffast-math+  build-depends:       base >= 4.8 && < 4.11+                     , imj-prelude ==0.1.*+                     , optparse-applicative == 0.14.*+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/OlivierSohn/hamazed/+  subdir:   imj-measure-stdout
+ src/Imj/Measure/Stdout.hs view
@@ -0,0 +1,188 @@++module Imj.Measure.Stdout+    ( testStdout+    , testStdoutSize -- for its doc+    , Settings(..)+    -- * Reexports+    ) where++import           Imj.Prelude++import           Control.Concurrent+import           System.IO+++data Settings = LengthTest {+  _settingsLengthTestLength :: !Int+}+               | OtherTests !Bool+++cursorForward, cursorBackward, noop:: String+cursorForward = "\ESC[C"+cursorBackward = "\ESC[D"+noop = cursorForward ++ cursorBackward++-- | Creates a string of given length, whith one visible character,+-- a very big number of no-op escape sequences (cursor forward, cursor backward)+-- and spaces to pad the string to the desired length.+mkSmartStringToExactlyFillBufferOfSize :: Char+                                       -- ^ the char that will be visible+                                       -> Int+                                       -- ^ the length of the string+                                       -> String+mkSmartStringToExactlyFillBufferOfSize char bufferSz =+  let noopSz = length noop+      bufferSzMinusCharSz = pred bufferSz+      countNoops = quot bufferSzMinusCharSz noopSz+      remainingSize = bufferSzMinusCharSz - countNoops * noopSz+      str = replicate remainingSize ' ' ++ [char] ++ concat (replicate countNoops noop)+  in if length str /= bufferSz+       then+         error $ "test logic : " ++ show (str,bufferSz)+       else+         str++{- | A function to visually see if the stdout size is smaller or bigger than given numbers.++@+if you see 1, then 2, then 3,+    size of stdout buffer < size+if you see 1, then 2 and 3 at the same time,+    size of stdout buffer >= size+@++see 'testStdoutSize' doc to understand how to determine the size.+-}+testStdoutSizes :: Int -> IO ()+testStdoutSizes size = do++  when (size <= 1) $ error $ "The size (" ++ show size ++ ") should be >= 2."++  print "test stdout size :"+  print " -if you see 1, then 2, then 3:"+  print $ "    maximum size of stdout buffer < " ++ show size+  print " -if you see 1, then 2 and 3 at the same time:"+  print $ "    maximum size of stdout buffer >= " ++ show size+  -- on my system, 8192 is the stdout max buffer size for+  -- BlockBuffering and LineBuffering modes+  mapM_ testStdoutSize [size]+  putStrLn ""++{- | This function, when run from a process whose stdout is the console,+can be used to visually determine the maximum capacity of stdout.++If stdout capacity >= sz, then in the console we will see++> 1++then++> 1  2  3++Else we see++> 1++then++> 1  2++then++> 1  2  3++There can be "0 to length 'noop'" spaces between chars, they are used+to pad the string sent to the buffer.+-}+testStdoutSize :: Int -> IO ()+testStdoutSize sz = do+  withBufferMode $ BlockBuffering $ Just sz++  let str = mkSmartStringToExactlyFillBufferOfSize '1' sz+      delay = 1000000+  putStr str+  threadDelay delay++  let str' = mkSmartStringToExactlyFillBufferOfSize '2' (pred sz)+  putStr str'+  threadDelay delay++  putChar '3'+  hFlush stdout+  threadDelay delay++withBufferMode :: BufferMode -> IO ()+withBufferMode b = do+  setBufferModeAndVerify b+  bufmode <- hShow stdout+  putStrLn ""+  putStrLn $ "Buffer Mode = " ++ bufmode+  putStrLn ""+  hFlush stdout+++testStdout :: Settings -> IO ()+testStdout (LengthTest candidateLength) = testStdoutSizes candidateLength+testStdout (OtherTests False) = error "missing option (use --help to see the different possibilities)"+testStdout (OtherTests True) = do+  let actions = [+                -- shows that the 'n' in "BlockBuffering (Just n)" is homogenous to+                -- a string length, containing both displayable characters and characters+                -- of escape sequences:+                -- for "BlockBuffering (Just 4)", each flush renders+                --     | cursorforward+                -- for "BlockBuffering (Just 5)", flush renders in alternance:+                --          | cursorforward |+                --          cursorforward | cursorForward+                -- It also shows that the \0 for the end of the string is not stored+                -- in the buffer (ie the length of the buffer used for cursorforward is 3)+                 (putChar '|' >> putStr cursorForward, "putChar '|' >> putStr cursorForward")++                -- shows the effect of buffering on displayable characters+                ,(putChar '|', "putChar '|'")++                -- shows the effect of explicit flushes+                ,(putChar '|' >> hFlush stdout, "putChar '|' >> hFlush stdout")+                ]+  mapM_ (uncurry testAction) actions++testAction :: IO () -> String -> IO ()+testAction action desc = do+  print $ "-- " ++ desc+  let runTests = runActionTests action+      modes =+        [ -- NoBuffering -- it seems equivalent to "BlockBuffering (Just 1)"+          BlockBuffering Nothing+        , BlockBuffering (Just 1)+        , BlockBuffering (Just 2)+        , BlockBuffering (Just 4)+        , BlockBuffering (Just 5)+        , BlockBuffering (Just 8)+        , BlockBuffering (Just 16)+        , LineBuffering+        ]++  mapM_ runTests modes++runActionTests :: IO () -> BufferMode -> IO ()+runActionTests action b = do+  withBufferMode b++  let tenthSecond = 400000+  every tenthSecond action 16++every :: Int -> IO () -> Int -> IO ()+every period action remaining+  | remaining == 0 = return ()+  | otherwise = do+      action+      threadDelay period+      every period action (pred remaining)++setBufferModeAndVerify :: BufferMode -> IO ()+setBufferModeAndVerify b = do+  hSetBuffering stdout b+  b' <- hGetBuffering stdout+  when (b /= b') $ error $ "Failed to set buffering: " ++ show (b,b')+  return ()