Minimalist C Libraries - <AUTHOR> http://nullprogram.com/blog/2018/06/10/ <comment><USER3> > "The library mustn't call malloc() internally. It's up to the caller to > allocate memory for the library. What's nice about this is that it's > completely up to the application exactly how memory is allocated. Maybe it's > using a custom allocator, or it's not linked against the standard library."

OP's approach will indeed work for most "minimalist"/single-header libraries, but, I personally feel it pollutes the API you're exposing to your users.

Depending on the specific situation, I may sometimes choose to expose a MODULE_CreateObject() and a MODULE_CreateObjectEx(custom_allocator, custom_deallocator).

Internally, MODULE_CreateObject() calls MODULE_CreateObjectEx(), passing the module's default allocators and deallocators (ie. HeapAlloc and HeapFree). This strikes me as a more balanced approach.

One caveat here, is that you must enforce consistency across usage - you don't want some API calls to use malloc() for allocation, whilst others use HeapFree() for deallocation, that would be a recipe for disaster.

To ensure that, I would often set the allocators and deallocators once, when the object is first created. They may be set through the object's initialization function, and they persist as part of the object itself.

<comment><USER4> > I personally feel it pollutes the API you're exposing to your users.

Pollution is in the eye of the beholder. There are _many_ circumstances where a project or subset of a project needs to work without a heap, they just don't necessarily overlap with the "application layer code in a virtual memory process" world your intuition is calibrated against.

And sometimes this stuff needs to read a JSON object or decode base64 or utf8 too, and can't because the library is too thick.

<comment><USER3> > "Pollution is in the eye of the beholder. There are many circumstances where > a project or subset of a project needs to work without a heap, they just > don't necessarily overlap with the "application layer code in a virtual > memory process" world your intuition is calibrated against."

That's an argument in favor of offloading allocation/deallocation to the library's users, which is exactly the core of my, and OP's, proposals. We're saying the same thing here - developers should be able to determine/control how memory is allocated and deallocated.

> "And sometimes this stuff needs to read a JSON object or decode base64 or > utf8 too, and can't because the library is too thick."

I'm losing you here. I honestly feel that my proposal is all about keeping the API as simple as humanely possible, without compromising the library's flexibility when it comes to the scenarios your mentioned earlier.

In your case:

      BASE64DECODER_Decode(...)
      BASE64DECODER_DecodeEx(..., allocator, deallocator)
      BYTE * BASE64DECODER_GetDecodedBuffer(handle)
      BASE64DECODER_Free(handle)

<comment><USER4> But what if I don't have a heap? Not even a wrappable heap.

I could be an OS bootstrapping layer, a signal handler, an ISR, a process control project operating under strict 'No dynamic allocation!" rules, a thunking layer to get legacy code modes (BIOS says hi!), ...[1]

You're imagining a world where everything is Node or Python or Java, or at the worst C on top of the well-defined standard library. And I'm telling you that the world is bigger than that.

And more specifically, that those weird layers sometimes need library code too.

[1] (Edited to add) A malware payload, a tracing layer, a compiler-generated stub, a benchmarking hook that can't handle heap latency, ...

<comment><USER3> > "You're imagining a world where everything is Node or Python or Java, or at > the worst C on top of the well-defined standard library. And I'm telling you > that the world is bigger than that."

Why do you keep putting words in my mouth?

> "But what if I don't have a heap? Not even a wrappable heap."

I'm forced to repeat myself over again. At no point does my proposed API force you to rely on a heap. On the contrary, it lets you rely on whatever solution works best for you, in your specific case.

In your custom kernel project, your custom allocator() can return a buffer from a memory pool you handle yourself. Your custom deallocator() will reclaim that memory back into your custom memory pool.

In a different project, say a desktop app for Windows 10, the allocator() will simply call malloc(), and the deallocator() will call free().

This way, your allocator() can do whatever. Your deallocator() can do whatever. How is this restrictive in any way shape or form?

<comment><USER5> > In your custom kernel project, your custom allocator() can return a buffer > from a memory pool you handle yourself. Your custom deallocator() will > reclaim that memory back into your custom memory pool.

I don't have either. I have a statically allocated buffer big enough for one frame of data, and I need to guarantee that it never gets used twice. My code does not have a custom allocator. It does not allocate.

<comment><USER3>
      void * your_custom_allocator(size_t size)
      {
          // Handle your locks.
          // Sanity checks, assertions, bounds checks, etc...

          void * result = g_your_buffer + g_position;

          // "Commit Memory" from your buffer.
          g_position += size;

          // Some more code...

          return result;
      }

Now, you can happily use:

      BASE64DECODE_DecodeEx(..., your_custom_allocator, your_custom_deallocator);

What else do you need? You seem very disturbed by the use of the word "allocator" here, feel free to rename to whatever works for you.

<comment><USER5> > What else do you need?

A guarantee that this "allocator function" is only ever called once.

<comment><USER3> Are you seriously suggesting this is an issue? That's entirely up to you to solve in your custom allocator.

Use whatever mechanism is available to you. Use a global condition variable, check it atomically every time you're entering your custom allocator, increment after a successful allocation. I don't know your system's constraints, nor should I...

<comment><USER5> I'm not talking about concurrency. I'm talking about needing to know _exactly_ how many bytes are being allocated ahead of time, because I've got 192k of ram, and 112k of them are spoken for by I/O buffers.

If I pass in an allocator that returns the statically allocated buffer, then the second call to it _must_ abort loudly.

</comment></comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER6> These "single-file libraries" share a lot of the same ideals: [https://github.com/nothings/single_file_libs](https://github.com/nothings/single_file_libs)

</comment><comment><USER7> The no malloc within the library cannot be overstated, let the user of the library decide what is best and do not do some junk like `some_fn()` and require an implicit `some_free()`

<comment><USER8> I disagree. This is an artificial limitation that severely hampers the library's capabilities. That kind of discipline may be fine for normal linear string-crunching, but it's cumbersome to an asinine degree if your library needs to do any sort of complex ADT manipulation.

<comment><USER9> It calls to mind Fortran-style function docs which would give a formula to compute the size of a work array for the caller to provide. That really ties the implementation to the header. It was really boring to work with, though it often pushed you to understand what was going on inside.

<comment><USER10> This kind of interface was common in fortran 77. I'm guessing you've used BLAS/LAPACK? In more modern fortran, it's not so usual. Also, Fortran is much more high level than C, and how memory is allocated etc is compiler dependent to a much larger degree than for C.

I'd recommend writing a Fortran 95 wrapper which creates work arrays for you if you really need to use such routines.

</comment></comment></comment></comment><comment><USER11> With respect to the bmp example: 1\. Removing the bmp_get implies that the application needs to implement a shadow image if it needs to check the color o a certain pixel. Or even worse, take a direct peek in its void memory area. 2\. void pointers instead of bmp_pointers makes it easier to create a mess, the compiler will not tell you that you called the bmp library with a pointer to a jpg memory area. 3\. Not doing range checking in the library - <USER2> imposing that burden on the caller - is a bad practice. If the caller does the same - expects his caller to do the checking - we end up with a sequrity risk.

Trying to minimize the library by pushing work to the application is wrong every time you expect the library to be used more than once. Despite these objections, I like libraries that are free of IO and mallocs!

<comment><USER12> When I fell down into the rabbit hole of DNS, I wrote code to just encode and decode DNS packets [1]. All the existing libraries [2] had a complex API that provided a separate function for querying a few record types (A, AAAA, MX, TXT, SRV, maybe NS and SOA), leaving the rest unimplemented. They also tend to have complex network architectures to handle retries, caching, and parallel queries which could be hard to integrate into a project that had an existing network framework.

Mine? Just two functions: dns_encode() and dns_decode(). No I/O. No malloc().

[1] [https://github.com/spc476/SPCDNS](https://github.com/spc476/SPCDNS)

[2] The ones I was looking at are written in C.

<comment><USER13> > No malloc().

...by having your own arena allocator! I do agree that it is quite doable in this particular case, but I always remember that a custom memory allocator of OpenSSL made Heartbleed much more devastating.

<comment><USER12> ... of memory passed in by the user! So it's up to the caller to make sure memory contains unclassified information.

</comment></comment></comment></comment><comment> loup-vaillant I'm happy to say my crypto library¹ satisfies most of his criteria:

It has 50 functions. That's too much, but it could be reduced to 10 if the user stick to the highest level facilities. There is no dynamic memory allocation, and no I/O (actually, it doesn't even depend on libc). The structures are defined in the header to allow the user to allocate them on the stack, but looking inside is unneeded and discouraged.

[1]: [https://monocypher.org](https://monocypher.org)

</comment><comment><USER14> It's worth pointing out that his two favorite RNGs (xoroshiro128+/xorshift128+) both fail BigCrush. According to [0] and the associated github [1], for a statistically strong RNG which is still fast, AES-CTR or splitmix64/lehmer64 are probably your best bet, unless you have AVX512, in which case an SIMD-accelerated PCG is the way to go [2]. (The other methods cap out at 1 cycle per byte, while the AVX512 PCG is 1 cycle per 32-bit integer, 4x as fast as the fastest previously tested.) While I don't doubt it could be further accelerated, I've added STL compatibility, templated unrolling, and provided some extra utilities (including random access) in a package based off code from [1] (provided by Samuel Neves) which I now use in most of my projects, and which is available at [3].

[0] [https://lemire.me/blog/2017/09/08/the-xorshift128-random- num...](https://lemire.me/blog/2017/09/08/the-xorshift128-random-number- generator-fails-bigcrush/)

[1] [https://github.com/lemire/testingRNG](https://github.com/lemire/testingRNG)

[2] [https://lemire.me/blog/2018/06/07/vectorizing-random- number-...](https://lemire.me/blog/2018/06/07/vectorizing-random-number- generators-for-greater-speed-pcg-and-xorshift128-avx-512-edition/)

[3] [https://github.com/dnbaker/aesctr](https://github.com/dnbaker/aesctr)

<comment><USER15> The way the mentioned PRNGs "fail" is when testing just the lower bits (search for the occurrences of "lsb" in [1] above) and this may be important in your use cases or not. The same [1] claims in "Visual Summary" that the "cycles/byte" is 1 for various PRNGs but [http://xoshiro.di.unimi.it/](http://xoshiro.di.unimi.it/) seems to show that the reason splitmix64 is not preferred everywhere is that xoroshiro128+ is roughly two times faster than splitmix64 .

Regarding having lower bits poor statistically, it was known since forever that that is the case for the huge class of simple PRNGs (effectively all that are faster than the alternatives, unless maybe if there's some specialized instruction in the CPU), the question is if that is critical or not for your purposes. The author of xoroshiro128+ is of course aware of that issue, and he also writes:

"For general usage, one has to consider that its lowest bits have low linear complexity and will fail linearity tests; however, low linear complexity can have hardly any impact in practice, and certainly has no impact at all if you generate floating-point numbers using the upper bits (we computed a precise estimate of the linear complexity of the lowest bits)."

In short, if you don't know how you're going to use the PRNG and you don't have problems related to speed, sure, use the safest one. Note that "safety" is still differently understood in different use cases, e.g. take care to note that most of fast PRNGs still aren't cryptographically secure:

[https://en.wikipedia.org/wiki/Cryptographically_secure_pseud...](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator)

and that sometimes even "standardized" "cryptographically secure" turn out to be something else, e.g. the subtitle on that wikipedia page:

"NSA kleptographic backdoor in the Dual_EC_DRBG PRNG"

Also other considerations come into play when you have some specific needs and you understand the consequences: then it's not only black-and-white "safe" v.s. "not safe." For some purposes (as the mentioned generation of the floating-point numbers in some use cases) speed matters enough to sacrifice some "perfectness."

<comment><USER14> I use fast RNGs for kernel projections, FHT-accelerated JL transforms, and data generation for numerical experiments. I don't need cryptographic security for these purposes.

<comment><USER15> And if you generate floating point numbers maybe you don't have to worry about lsbs alone either? The "failed" tests drop away the bits that matter the most when floating point randomness is constructed.

<comment><USER14> I don't know the details on which bits matter in the ziggurat algorithm, which is the one I use. Is this the case for all floating point random number generators?

<comment><USER15> The PRNGs you mentioned before all generate integers. The conversion from the integer PRNG to the floating point, and the conversion from one integer range to another range needed in the ziggurat algorithm both need to be done "right" to give correct results (I can imagine that even using splitmix64 the wrong implementation could be programmed by somebody not knowing what has to be done), so if you aren't sure about these steps you should surely check their quality yourself. If these are done right, I'd personally expect xoroshiro128+ could be "good enough" even when having that specific weakness (poorer quality when using only lsb bits) that you worried about. It's important, of course, not to drop the highest bits away.

And on another side, 2om3r questions the speed measurements of the author, and I think he has a point: the speed measurements should be made in the context of the real use, otherwise the compilers are able to "cheat" (optimize pieces of the code away) if the example is too simple.

</comment></comment></comment></comment></comment></comment><comment><USER16> This man deserves a monument. The number of such disciplined C coders who can articulate what a proper C coding is is dwindling with each year.

<comment><USER17> Its a style of programming C was built for, and not many other languages.

It's almost the opposite of good style elsewhere: aggressively procedural code with an elegant boundary.

I'd say it would be a good lesson to many who are down the or pure functional rabbit holes: perhaps there is some other sense of modularity that goes missing when "single purpose" is applied too narrowly.

<comment><USER18> > Its a style of programming C was built for, and not many other languages.

Rust seems well adapted for it:

> (1) Small number of functions, perhaps even as little as one.

One annoyance with that kind of stuff is that you'll get many small dependencies rather than a few big ones. Having a simple and easy way to acquire & maintain these dependencies is useful, and Cargo provides that.

> (2) No dynamic memory allocations.

> (3) No input or output.

That's pretty much what a #[no_std] (libcore-only) library is[0].

> (4) Define at most one structure, and perhaps even none.

That's the bit I disagree most about, but if that's what you want the language won't stop you.

[0] unless it's requires nightly and uses alloc directly but that's not too common right now I think

</comment></comment></comment><comment><USER19> I just want to understand that given the mostly negative reaction that's here at HN to the npm ecosystem which is based on the same "minimalist libraries" idea, how is this different?

I'd appreciate if the response is not about JS and/or C, but about the minimalist libraries in JS, C, or any other language. Should I use a large number of small libraries? Should I wrap up some of the code which I use into a libraries even if that code is just a couple of functions without any data structure?

Moreover, I'd admit that I'm a great fan of Chris Wellons blog posts which are pretty technical and original, and use some of his emacs libraries on a daily basis.

<comment><USER20> > _I just want to understand that given the mostly negative reaction that 's > here at HN to the npm ecosystem which is based on the same "minimalist > libraries" idea, how is this different?_

It's different in that in C you don't pull in 200 dependencies which in-turn bring in another 10+ dependencies each.

You just use 2-3 libs you need (and that they, in turn, don't require anything, or at best the POSIX standard libs), and that's it.

</comment></comment><comment><USER21> Love reading this stuff. When I grow up I want to be a C programmer.

</comment><comment><USER22> Maybe I'm just noticing it more, but I think there's more discussion of how to write C well than there used to be. I speculate that the attention Rust has brought to safe systems programming has caused an uptick in interest in closer-to-the-metal languages in general, and spurred C programmers to show that there are reasonable ways to write C as well. It may be an unanticipated result of Rust's popularity that the quality of C programming improves. (Or perhaps that was the plan all along?)

<comment><USER23> What I've noticed is the lead time for all sorts of things used in small embedded systems has gotten terrifyingly long. What says to me that there is a lot of embedded work going on.

Also in the last 5 years people have abandoned the JVM as a end all be all platform which puts you squarely back into native code again.

</comment></comment><comment><USER24> Most things here are reasonable, but I don't see a point about having only one struct. If your state is better organized in lots of hierarchical structs, within lists, within other structs, your data will be easier to move around, copy, and zero in smaller chunks, and you can write functions which processes isolated segments of data rather than a huge global state.

<comment><USER25> The point was about the user-facing side not the internal representation of state.

<comment><USER24> Even then, it seems weird to prefer a big flat state rather than a state made of heirarchical sub-structures and arrays.

<comment><USER26> As the user of the library I _probably_ don't care about the hierarchies. If I want X the library can provide me a function to get X by looking up its internal substructures and arrays so that I don't have to.

</comment></comment></comment></comment><comment><USER27> This basically echoes my library building philosophy. The two biggest things are:

1\. User-facing complexity: Keep the user interface just big enough to get the job done. Put the "90% of people" interface first and foremost and if you need to cover the other 10%, expose a different interface that's CLEARLY marked "Advanced. You probably don't need to use this". Don't get sucked into chrome plating everything.

2\. Internal complexity: Keep your structures simple. Make your functions do one thing and exactly that thing, well. Keep your side effects to a minimum. And keep your dependencies low, because you can't trust that other people have done the same in their libraries.

</comment><comment><USER28> I don't think there are languages similar to C in term of simplicity, close to the metal and speed.

C++ is good enough for me, but it's so slow to compile, and I don't use its most advanced features.

I wish there was a language between C and C++, without the complex semantics you can find in rust and other exotic syntax.

I don't necessarily love C or C++ in term of feature, but the syntax is just what i need. Why can't language designers write a language that is closer to C, with fancy features that don't change the language so much?

<comment><USER29> Maybe you would like the BetterC mode in the D programming language... if you mean you like C for it's syntax too. A lot of modern systems prog. languages seem to adopt a more modern syntax, i.e. types after the names rather than before, no semi-colons, etc.

D stays true to C in this regard and offers a lot of fancy features. And the BetterC mode sounds suited to your requirements in that the language features doesn't over complicate things.

</comment></comment><comment><USER30> Personally, I'd add "don't write to your own memory" (with various stack use rules based on expected library use) and relax the "no structures" rule to encourage code that can be used from multiple threads simultaneously. Make the first argument always be the internal use data.

</comment><comment><USER31> I'd argue these APIs could be further minimized by using a struct with a void * and a size_t in it, along with bounds checking accessors.

This would eliminate most of the ugliness in the post-allocation calls, allow for the deletion of most error checking code in each library, and would harden BMP parsing "for free".

</comment><comment><USER32> Please do not do this! There is so much bad advice in this article. Remember the rule: Your code should be simple, but not simpler. There is absolutely no need to abandon great facilities afforded by language and libraries to make things unreadable, undebugable and unmaintainable.

<comment><USER18> Abandoning _some_ facilities does not "make things unreadable, undebugable and unmaintainable" and can allow them being used much more widely.

The limitations seem similar to #[no_std] in Rust, and while that's not something to strive for at all costs if you can do without it allows e.g. embedded developers or kernel/OS developers to use the work.

<comment><USER33> > e.g. embedded developers or kernel/OS developers to use the work.

I certainly agree that's one of the strongest reasons to avoid allocating memory etc. It's pretty clear that not many commenters have done any work outside a hosted environment... but I guess that makes the point that we're in fairly specialised territory.</comment></comment></comment>


Dropbox adds a device limit to free accounts - <AUTHOR> https://www.dropbox.com/help/account/computer-limit <comment><USER2> I'm a little sad that there's no low price offering comparable to Google drives 2$/100GB. I just don't need a TB of personal space but I like the dropbox client a lot better. Especially because of linux support and for some reason syncing seems to be significantly faster.

<comment><USER3> Dropbox uses full differential syncing, the only mainstream client that does that universally. Onedrive only differential syncs with office files. Google drive, well, uses full file syncing for every file.

<comment><USER4> Could you please elaborate on what differential syncing means? Does Dropbox break down each file into small chunks before uploading? What are the advantages?

<comment><USER5> [https://www.dropbox.com/help/syncing-uploads/upload- entire-f...](https://www.dropbox.com/help/syncing-uploads/upload-entire-file)

This should save some bandwidth at the expense of CPU time.

</comment></comment></comment></comment><comment><USER4> I don't understand the Dropbox negativity on HN. I've used it since 2010 and it has provided me with incredible value. I used to have free service for documents but my library of data grew larger. I signed up for their $10/month program without a hitch and have been a paying user for years. You get a terabyte of space.

I got rid of NAS backups and I have selective sync enabled on folders that I need synced. Large files remain in the cloud.

There are a few things that I think are worth paying for. Dropbox delivers every day, every hour with no worries of backups.

In terms of limiting devices, remember when iTunes introduced 5 authorized devices? Spotify Free has ads all over. Dropbox's business model is to convert free paying users by exemplifying the value that they provide without excessive nags, ads or annoyances.

<comment><USER6> There has been several things:

• Dropbox has failed to improve their offering and even for a paying customer you do not get anything more for your money today than you were getting several years ago. That is not objectively a bad thing, I mean a spoon has hardly evolved in hundred of years and is still worth every penny. But this is HN and that's a bit of a sin around here.

• In search of profitability Dropbox has used several dark marketting patterns. Its website also pushes way too much business/team option that are per user/per month. A lof of people on HN are private user and they basically have access to a single option.

• The new features that Dropbox did introduce work only for corporate/team user. There is now a Professional user but it costs a standard business plan for 2 users with less feature.

• There was some complain about the Mac OS client uses of apis.

Despite the negativity, HN users seems major paying users. So I think rather than negativity it is more disappointment. The feeling that Dropbox could have been so much more and they would have paid for it gladly, but it is instead becoming a network feature for large companies and startup.

<comment><USER4> I think you could say the same thing about GMail. It looks and works the same way as it did 15 years ago. At times, less is more and feature creep ends up being like Microsoft Outlook - bloated, slow and frustrating to use.

I don't love my set of spoons like I love Dropbox, Digital Ocean, Eames Lounge Chair, Sublime Text/Merge and Teenage Engineering synthesizers. There are a handful of companies that give you immense pleasure in using their products daily.

<comment><USER7> Gmail changed a lot and its new design is just great although some people might disagree with me. It's also free.

</comment></comment></comment></comment><comment><USER8> I've been using free Dropbox for a long time. After hitting my storage limits about year ago, I started learning about alternatives for the paid plans. Ended up with self hosted Nextcloud ( [https://nextcloud.com/](https://nextcloud.com/) ), which is open source. Been super happy with Nextcloud, would definitely recommend it for anyone who isn't fully happy with Dropbox.

The clients for macOS and iOS are not as polished as Dropbox and many apps use Dropbox integrations, but I've managed to find workarounds for everything except 1Password, which is why I'm currently looking for alternative (preferrably open source) solutions for it.

For me the self hosting is not a problem and there are several hosted options available as well. (For example: [https://www.hetzner.com/storage/nextcloud](https://www.hetzner.com/storage/nextcloud) )

<comment><USER9> > I've managed to find workarounds for everything except 1Password

I haven't personally used it, but I've seen Bitwarden being highly recommended here. It's free and open source. It has a free option (with an account on bitwarden.com), a paid hosting option (with an account on bitwarden.com) and also a self-hosting option.

</comment></comment><comment><USER10> What is dropbox basic? I see no explanation anywhere.

It's been ages since I visited dropbox but my god how things have changed. Every flow and part of their website is about converting people to paid.

<comment><USER11> The nerve! It's almost like they want to make money!

<comment><USER12> Because Dropbox wants to make money, we should all happily put up with steady annoyance?

</comment></comment></comment><comment><USER13> That's a little inconvenient, but I'm running on three devices at the moment.

Now can we get a .dropboxignore file for developers please? I don't need the temporary files in my projects being synced.

</comment><comment><USER14> They freed up the half-dozen devices on my account when they stopped supporting anything other than vanilla ext4 on Linux.

<comment><USER15> Wait, what? Why should the Dropbox client care which filesystem you are using?

<comment><USER16> I think it's something to do with extended attributes - though why they need them now, after all these years, I don't know.

<comment><USER7> For differential sync maybe?

</comment></comment></comment></comment><comment><USER9> > If you're a Basic user and you linked more than three devices prior to March > 2019, all of your previously linked devices will remain linked, but you > can't link additional devices.

For how long will the previously linked devices remain linked...in other words, when will Dropbox force basic plan users to reduce the count to three? I foresee that happening in the future.

And what does this mean for people who change devices (like get a new phone to replace the old one)? Do they have to use the same device name or can they unlink the older device and link the new device in its place? This page does not talk about this scenario.

Seems like it's time to move out of Dropbox.

<comment><USER17> AIUI they're not claiming to ever remove already-linked devices, but you can't add more devices without going back under that threshold.

</comment></comment><comment><USER18> It seems, time to use Amazon Drive: $12 for 100Gb annual. I was dropbox paying customers for years but stopped my subscription in 2016 in favor of Google Photos and iCloud. I realized there's nothing to store there other than a general photos/videos, all my docs takes less than a few gigabytes of data. Also I was changed for nothing, like restore file deleted a minute ago.

<comment><USER19> There are multiple alternatives to dropbox which are cheaper, but which are giving the same or better client and user experience?

</comment></comment><comment><USER20> What is the actual Limit? I am redirected to the German Version which states "no limit"

<comment><USER21> you can switch the language at the bottom left side of the page.

the limit is three.

</comment></comment><comment><USER22> Fortunately, some 3rd party tools like CloudMounter can still connect Dropbox to an unlimited amount of devices.

</comment><comment><USER23> "Devices" are both PCs and smartphones, is that correct?

<comment><USER24> I'd assume so, given the URL itself is "[https://www.dropbox.com/help/account/computer- limit"](https://www.dropbox.com/help/account/computer-limit")

<comment><USER25> Considering smartphones don't even sync anything by default, why should they be counted as devices. The app is just a glorified website wrapper.

<comment><USER26> Exactly. We've been waiting for years for a real Dropbox client for smartphones, and now this.

I think you can you can activate sync for individual files. But as you have to activate this for each file individually, this it somewhat defeats the point. There are more options in the pro version, but why then count it towards the limit for free users.

I hardly even use dropbox on mobile anyway, it just happens to be one of the most convenient ways to transfer pdfs to my iPad.

</comment></comment></comment></comment><comment><USER27> so, whatever happened to btsync?

<comment><USER28> It turned into [https://www.resilio.com/](https://www.resilio.com/)

<comment><USER29> Beware that I deployed it in an enterprise environment (after a successful pilot and having used it successfully on my, small, network for months) and it often failed when tasked with rapidly changing files from multiple different clients. This was on a 30-client/3 buildings on a point-to-point VPN setup. The customer lost data on regular basis and I had to back out from the solution. This was when it still was btsync, and may be it meanwhile has improved, but just test it with a significant number of client and concurrent edits before trying a (relatively) big deploy.

</comment></comment></comment><comment><USER30> Doomed</comment>


Show HN: Improving Reading Focus by Dimming Online Distractions - <AUTHOR> https://medium.com/@BeeLineReader/improving-reading-focus-by-dimming-online-distractions-ae52726abe1#.y9k711ihi <comment><AUTHOR> creator here: Just added a big new feature to our Chrome extension (which got its start on HN several years ago). Looking forward to hearing your thoughts on both the concept and the technical implementation!</comment>


Ask HN: How do you feel about sites that deny access to adblockers? - <AUTHOR> Do publishers have a right to reserve their content only to those who support their advertisers, or should all content be free until some other kind of business model could be devised? <comment><USER2> >> /dev/null

</comment><comment><USER3> if it can be done, I block the paywall with my adblocker</comment>


Google your lost Android phone - <AUTHOR> https://plus.google.com/+google/posts/CEdEWkg4dvf

<comment><USER2> Is this supposed to just be GWS? It's not working for me. (Of course, I have the interface set to Spanish, which has occasionally kept things like "weather" from working as well.)

EDIT: [https://www.google.com/android/devicemanager](https://www.google.com/android/devicemanager) works fine. Just not doing anything from GWS.

<comment><USER3> Yes. It works right in the standard Google homepage. (For me it requests I sign in again for security first)

</comment></comment><comment><USER4> Other than a front page macro, is there a new feature? Obviously the Android Device Manager has been around for a long time, but could you always use it to ring your phone? I could have sworn I saw the 'ring' option on the tracking page at least several weeks ago.

</comment><comment><AUTHOR> We've all been there — you've searched under your car seat, tossed around the sofa cushions and you still can't find your phone. If you know where your computer is, you can now ask Google to find your Android phone from your desktop. If the pesky phone is hiding nearby, Google can ring it for you — or you can see it on the map if you, say, forgot it at the bar. Just make sure you've got the latest version of the Google app installed on your device!

<comment><USER5> The ringing feature works as long as your phone has background data turned on (I think). But the location feature needs you to have location turned on as well.

</comment></comment><comment><USER6> It makes me log in again to use it. That's fine except that I have 2FA, the codes for which are generated by my phone.

Now, yes, I can use Authy to go and get them, but I suspect that if I were using Google Authenticator I'd still be without a phone.

<comment><USER7> You could use backup codes or the account recovery procedures in that case.</comment></comment>


Jenkins Is Getting Old – and We Need an Alternative - <AUTHOR> https://microtica.com/jenkins-is-old-we-need-an-alternative/ <comment><USER2> As others have noted this is a thinly veiled PR piece (nothing wrong with it, but the angle is not immediately apparent).

From the post:

> With Jenkins, you can try and build a CD platform yourself, but be aware > you'll need a whole team for it, a load of time and maintenance. Microtica > enables you to define complex cloud infrastructure through full integration > with AWS and Kubernetes. Automate your deployments and deliver on the cloud > with confidence.

Not surprising then that it doesn't even mention all the widely used alternatives: CircleCI, GitLab, Travis, etc.

</comment><comment><USER3> There are... many alternatives? CircleCI, BuildKite, Travis, GitLab, GitHub Actions, Harness, and many more. Has been the case for years. Microtica is a little late to the party methinks.

</comment><comment><USER4> Jenkins is fantastic, my only issue with it is that I can't decide if I like Groovy or hate it.

This is really bad form though, advertising your proprietary product by bashing a popular open source version. Your build process is something you don't want to have to rewrite just because some executive got taken out to lunch. I can't imagine any non free alternative gaining any traction.

<comment><USER5> I've written quite a bit of Groovy code and IME that never changes. It's a very pragmatic and productive language, but whenever you run into some bug or weird edge case you'll rue the day James Strachan was born. :)

</comment></comment><comment><USER6> This is basically a commercial right?

</comment><comment><USER7> I don't see the suggestion as a viable replacement for Jenkins in its entirety. They identified that the Free aspect of Jenkins leads to its popularity, and they both compliment and criticize the plugins ecosystem. Their don't appear to have an open solution, and while they claim you won't need plugins, but part of what makes Jenkins so popular, is that if the default functionality doesn't work, you can find or make a plugin. This is lacking in the proposed solution.

</comment><comment><USER8> "Code doesn't rust" and if Jenkins still works fine, why replace it?

<comment><USER9> Because the CI and hosting platform ecosphere has changed a lot in the last 10 years. If Jenkins has not adapted to suit the needs of devops engineers, then engineers will find something that does.

<comment><USER10> You can put pipelines into Git. What else do you need?

</comment></comment></comment><comment><USER11> I'm completely ignorant in Jenkins regard, but does it do anything extra that Gitlab CI/Github Actions/Bitbucket Pipelines & co doesn't do?

<comment><USER12> You can buy Jenkins via capex vs depend on the internet and subscribe monthly on the other?

<comment><USER13> Gitlab does have a self hosted option, though it is still a subscription.

<comment><USER14> You can self host Gitlab entirely, no subscription needed.

<comment><USER13> Agreed, though the paid plan might have more parity with whatever they were doing with Jenkins.

</comment></comment></comment></comment></comment><comment><USER15> These days, I mostly use Github Actions for build and something like deployer or Octopus Deploy.

In the past with on-prem build farms, I would use GoCD ([https://www.gocd.org/](https://www.gocd.org/))

</comment><comment><USER16> Consider [https://zuul-ci.org/](https://zuul-ci.org/) , perhaps?</comment>